]>
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>
27 #include "openvswitch/hmap.h"
28 #include "openvswitch/vlog.h"
31 VLOG_DEFINE_THIS_MODULE(dns_resolve
);
33 /* Guard all_reqs__ and resolve_state of each request. */
34 static struct ovs_mutex dns_mutex__
= OVS_MUTEX_INITIALIZER
;
35 static struct hmap all_reqs__
;
36 static struct ub_ctx
*ub_ctx__
;
38 static bool thread_is_daemon
;
40 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 1);
49 struct resolve_request
{
50 struct hmap_node hmap_node
; /* node for all_reqs__ */
51 char *name
; /* the domain name to be resolved */
52 char *addr
; /* the resolved ip address */
53 enum resolve_state state
; /* state of this request */
54 time_t time
; /* resolving time */
55 struct ub_result
*ub_result
; /* the stored unbound result */
58 static struct resolve_request
*resolve_find_or_new__(const char *name
)
59 OVS_REQUIRES(dns_mutex__
);
60 static bool resolve_check_expire__(struct resolve_request
*req
)
61 OVS_REQUIRES(dns_mutex__
);
62 static bool resolve_check_valid__(struct resolve_request
*req
)
63 OVS_REQUIRES(dns_mutex__
);
64 static bool resolve_async__(struct resolve_request
*req
, int qtype
)
65 OVS_REQUIRES(dns_mutex__
);
66 static void resolve_callback__(void *req
, int err
, struct ub_result
*)
67 OVS_REQUIRES(dns_mutex__
);
68 static bool resolve_result_to_addr__(struct ub_result
*result
, char **addr
);
69 static bool dns_resolve_sync__(const char *name
, char **addr
);
71 /* Pass a true 'is_daemon' if you don't want the DNS-resolving to block the
75 dns_resolve_init(bool is_daemon
)
77 ub_ctx__
= ub_ctx_create();
78 if (ub_ctx__
== NULL
) {
79 VLOG_ERR_RL(&rl
, "Failed to create libunbound context, "
80 "so asynchronous DNS resolving is disabled.");
86 retval
= ub_ctx_resolvconf(ub_ctx__
, "/etc/resolv.conf");
88 VLOG_WARN_RL(&rl
, "Failed to read /etc/resolv.conf: %s",
93 /* Handles '/etc/hosts' on Linux and 'WINDIR/etc/hosts' on Windows. */
94 retval
= ub_ctx_hosts(ub_ctx__
, NULL
);
96 VLOG_WARN_RL(&rl
, "Failed to read etc/hosts: %s",
100 ub_ctx_async(ub_ctx__
, true);
101 hmap_init(&all_reqs__
);
102 thread_is_daemon
= is_daemon
;
105 /* Returns true on success. Otherwise, returns false and the error information
106 * can be found in logs. If there is no error information, then the resolving
107 * is in process and the caller should call again later. The value of '*addr'
108 * is always nullified if false is returned. If this function is called under
109 * daemon-context, the resolving will undergo asynchronously. Otherwise, a
110 * synchronouse resolving will take place.
112 * This function is thread-safe.
114 * The caller is responsible for freeing the returned '*addr'.
117 dns_resolve(const char *name
, char **addr
)
118 OVS_EXCLUDED(dns_mutex__
)
120 bool success
= false;
122 if (!thread_is_daemon
) {
123 return dns_resolve_sync__(name
, addr
);
127 ovs_mutex_lock(&dns_mutex__
);
129 if (ub_ctx__
== NULL
) {
133 /* ub_process is inside lock as it invokes resolve_callback__. */
134 int retval
= ub_process(ub_ctx__
);
136 VLOG_ERR_RL(&rl
, "dns-resolve error: %s", ub_strerror(retval
));
140 struct resolve_request
*req
;
141 req
= resolve_find_or_new__(name
);
142 if (resolve_check_valid__(req
)) {
143 *addr
= xstrdup(req
->addr
);
145 } else if (req
->state
!= RESOLVE_PENDING
) {
146 success
= resolve_async__(req
, ns_t_a
);
149 ovs_mutex_unlock(&dns_mutex__
);
154 dns_resolve_destroy(void)
156 if (ub_ctx__
!= NULL
) {
157 /* Outstanding requests will be killed. */
158 ub_ctx_delete(ub_ctx__
);
161 struct resolve_request
*req
;
162 HMAP_FOR_EACH(req
, hmap_node
, &all_reqs__
) {
163 ub_resolve_free(req
->ub_result
);
168 hmap_destroy(&all_reqs__
);
172 static struct resolve_request
*
173 resolve_find_or_new__(const char *name
)
174 OVS_REQUIRES(dns_mutex__
)
176 struct resolve_request
*req
;
178 HMAP_FOR_EACH_IN_BUCKET(req
, hmap_node
, hash_string(name
, 0),
180 if (!strcmp(name
, req
->name
)) {
185 req
= xzalloc(sizeof *req
);
186 req
->name
= xstrdup(name
);
187 req
->state
= RESOLVE_INVALID
;
188 hmap_insert(&all_reqs__
, &req
->hmap_node
, hash_string(req
->name
, 0));
193 resolve_check_expire__(struct resolve_request
*req
)
194 OVS_REQUIRES(dns_mutex__
)
196 return time_now() > req
->time
+ req
->ub_result
->ttl
;
200 resolve_check_valid__(struct resolve_request
*req
)
201 OVS_REQUIRES(dns_mutex__
)
204 && req
->state
== RESOLVE_GOOD
205 && !resolve_check_expire__(req
));
209 resolve_async__(struct resolve_request
*req
, int qtype
)
210 OVS_REQUIRES(dns_mutex__
)
212 if (qtype
== ns_t_a
|| qtype
== ns_t_aaaa
) {
214 retval
= ub_resolve_async(ub_ctx__
, req
->name
,
216 resolve_callback__
, NULL
);
218 req
->state
= RESOLVE_ERROR
;
221 req
->state
= RESOLVE_PENDING
;
229 resolve_callback__(void *req_
, int err
, struct ub_result
*result
)
230 OVS_REQUIRES(dns_mutex__
)
232 struct resolve_request
*req
= req_
;
234 if (err
!= 0 || (result
->qtype
== ns_t_aaaa
&& !result
->havedata
)) {
235 req
->state
= RESOLVE_ERROR
;
236 VLOG_ERR_RL(&rl
, "%s: failed to resolve", req
->name
);
240 /* IPv4 address is empty, try IPv6. */
241 if (result
->qtype
== ns_t_a
&& !result
->havedata
) {
242 ub_resolve_free(result
);
243 resolve_async__(req
, ns_t_aaaa
);
248 if (!resolve_result_to_addr__(result
, &addr
)) {
249 req
->state
= RESOLVE_ERROR
;
250 VLOG_ERR_RL(&rl
, "%s: failed to resolve", req
->name
);
254 ub_resolve_free(req
->ub_result
);
257 req
->ub_result
= result
;
259 req
->state
= RESOLVE_GOOD
;
260 req
->time
= time_now();
264 resolve_result_to_addr__(struct ub_result
*result
, char **addr
)
266 int af
= result
->qtype
== ns_t_a
? AF_INET
: AF_INET6
;
267 char buffer
[INET6_ADDRSTRLEN
];
269 /* XXX: only the first returned IP is used. */
270 if (inet_ntop(af
, result
->data
[0], buffer
, sizeof buffer
)) {
271 *addr
= xstrdup(buffer
);
276 return (*addr
!= NULL
);
280 dns_resolve_sync__(const char *name
, char **addr
)
284 if (ub_ctx__
== NULL
) {
285 dns_resolve_init(false);
286 if (ub_ctx__
== NULL
) {
291 struct ub_result
*result
;
292 int retval
= ub_resolve(ub_ctx__
, name
, ns_t_a
, ns_c_in
, &result
);
295 } else if (!result
->havedata
) {
296 ub_resolve_free(result
);
298 retval
= ub_resolve(ub_ctx__
, name
, ns_t_aaaa
, ns_c_in
, &result
);
301 } else if (!result
->havedata
) {
302 ub_resolve_free(result
);
307 bool success
= resolve_result_to_addr__(result
, addr
);
308 ub_resolve_free(result
);