]>
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 *filename
= getenv("OVS_RESOLV_CONF");
88 /* On Windows, NULL means to use the system default nameserver. */
90 filename
= "/etc/resolv.conf";
94 if (!filename
|| !stat(filename
, &s
) || errno
!= ENOENT
) {
95 int retval
= ub_ctx_resolvconf(ub_ctx__
, filename
);
97 VLOG_WARN_RL(&rl
, "Failed to read %s: %s",
98 filename
? filename
: "system default nameserver",
100 ub_ctx_delete(ub_ctx__
);
105 VLOG_WARN_RL(&rl
, "Failed to read %s: %s",
106 filename
, ovs_strerror(errno
));
107 ub_ctx_delete(ub_ctx__
);
112 /* Handles '/etc/hosts' on Linux and 'WINDIR/etc/hosts' on Windows. */
113 int retval
= ub_ctx_hosts(ub_ctx__
, NULL
);
115 VLOG_WARN_RL(&rl
, "Failed to read etc/hosts: %s",
116 ub_strerror(retval
));
119 ub_ctx_async(ub_ctx__
, true);
120 hmap_init(&all_reqs__
);
121 thread_is_daemon
= is_daemon
;
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.
131 * This function is thread-safe.
133 * The caller is responsible for freeing the returned '*addr'.
136 dns_resolve(const char *name
, char **addr
)
137 OVS_EXCLUDED(dns_mutex__
)
139 bool success
= false;
141 if (!thread_is_daemon
) {
142 return dns_resolve_sync__(name
, addr
);
146 ovs_mutex_lock(&dns_mutex__
);
148 if (ub_ctx__
== NULL
) {
152 /* ub_process is inside lock as it invokes resolve_callback__. */
153 int retval
= ub_process(ub_ctx__
);
155 VLOG_ERR_RL(&rl
, "dns-resolve error: %s", ub_strerror(retval
));
159 struct resolve_request
*req
;
160 req
= resolve_find_or_new__(name
);
161 if (resolve_check_valid__(req
)) {
162 *addr
= xstrdup(req
->addr
);
164 } else if (req
->state
!= RESOLVE_PENDING
) {
165 success
= resolve_async__(req
, ns_t_a
);
168 ovs_mutex_unlock(&dns_mutex__
);
173 dns_resolve_destroy(void)
175 if (ub_ctx__
!= NULL
) {
176 /* Outstanding requests will be killed. */
177 ub_ctx_delete(ub_ctx__
);
180 struct resolve_request
*req
, *next
;
181 HMAP_FOR_EACH_SAFE (req
, next
, hmap_node
, &all_reqs__
) {
182 ub_resolve_free(req
->ub_result
);
187 hmap_destroy(&all_reqs__
);
191 static struct resolve_request
*
192 resolve_find_or_new__(const char *name
)
193 OVS_REQUIRES(dns_mutex__
)
195 struct resolve_request
*req
;
197 HMAP_FOR_EACH_IN_BUCKET(req
, hmap_node
, hash_string(name
, 0),
199 if (!strcmp(name
, req
->name
)) {
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));
212 resolve_check_expire__(struct resolve_request
*req
)
213 OVS_REQUIRES(dns_mutex__
)
215 return time_now() > req
->time
+ req
->ub_result
->ttl
;
219 resolve_check_valid__(struct resolve_request
*req
)
220 OVS_REQUIRES(dns_mutex__
)
223 && req
->state
== RESOLVE_GOOD
224 && !resolve_check_expire__(req
));
228 resolve_async__(struct resolve_request
*req
, int qtype
)
229 OVS_REQUIRES(dns_mutex__
)
231 if (qtype
== ns_t_a
|| qtype
== ns_t_aaaa
) {
233 retval
= ub_resolve_async(ub_ctx__
, req
->name
,
235 resolve_callback__
, NULL
);
237 req
->state
= RESOLVE_ERROR
;
240 req
->state
= RESOLVE_PENDING
;
248 resolve_callback__(void *req_
, int err
, struct ub_result
*result
)
249 OVS_REQUIRES(dns_mutex__
)
251 struct resolve_request
*req
= req_
;
253 if (err
!= 0 || (result
->qtype
== ns_t_aaaa
&& !result
->havedata
)) {
254 ub_resolve_free(result
);
255 req
->state
= RESOLVE_ERROR
;
256 VLOG_ERR_RL(&rl
, "%s: failed to resolve", req
->name
);
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
);
268 if (!resolve_result_to_addr__(result
, &addr
)) {
269 ub_resolve_free(result
);
270 req
->state
= RESOLVE_ERROR
;
271 VLOG_ERR_RL(&rl
, "%s: failed to resolve", req
->name
);
275 ub_resolve_free(req
->ub_result
);
278 req
->ub_result
= result
;
280 req
->state
= RESOLVE_GOOD
;
281 req
->time
= time_now();
285 resolve_result_to_addr__(struct ub_result
*result
, char **addr
)
287 int af
= result
->qtype
== ns_t_a
? AF_INET
: AF_INET6
;
288 char buffer
[INET6_ADDRSTRLEN
];
290 /* XXX: only the first returned IP is used. */
291 if (inet_ntop(af
, result
->data
[0], buffer
, sizeof buffer
)) {
292 *addr
= xstrdup(buffer
);
297 return (*addr
!= NULL
);
301 dns_resolve_sync__(const char *name
, char **addr
)
305 if (ub_ctx__
== NULL
) {
306 dns_resolve_init(false);
307 if (ub_ctx__
== NULL
) {
312 struct ub_result
*result
;
313 int retval
= ub_resolve(ub_ctx__
, name
, ns_t_a
, ns_c_in
, &result
);
316 } else if (!result
->havedata
) {
317 ub_resolve_free(result
);
319 retval
= ub_resolve(ub_ctx__
, name
, ns_t_aaaa
, ns_c_in
, &result
);
322 } else if (!result
->havedata
) {
323 ub_resolve_free(result
);
328 bool success
= resolve_result_to_addr__(result
, addr
);
329 ub_resolve_free(result
);