2 * Unix SMB/CIFS implementation.
3 * Internal DNS query structures
4 * Copyright (C) Volker Lendecke 2018
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "libcli/dns/dns_lookup.h"
22 #include "libcli/dns/resolvconf.h"
23 #include "libcli/dns/libdns.h"
24 #include "lib/util/tevent_unix.h"
25 #include "lib/util/samba_util.h"
26 #include "lib/util/debug.h"
28 struct dns_lookup_state
{
29 struct tevent_context
*ev
;
31 enum dns_qclass qclass
;
35 size_t num_nameservers
;
38 struct tevent_req
**dns_subreqs
;
39 struct tevent_req
*wait_subreq
;
41 struct dns_name_packet
*reply
;
44 static int dns_lookup_send_next(struct tevent_req
*req
);
46 static void dns_lookup_done(struct tevent_req
*subreq
);
47 static void dns_lookup_waited(struct tevent_req
*subreq
);
49 struct tevent_req
*dns_lookup_send(TALLOC_CTX
*mem_ctx
,
50 struct tevent_context
*ev
,
53 enum dns_qclass qclass
,
56 struct tevent_req
*req
;
57 struct dns_lookup_state
*state
;
58 FILE *fp
= resolv_conf_fp
;
61 req
= tevent_req_create(mem_ctx
, &state
, struct dns_lookup_state
);
67 state
->qclass
= qclass
;
70 if (resolv_conf_fp
== NULL
) {
71 const char *resolvconf
= "/etc/resolv.conf";
73 #ifdef ENABLE_SELFTEST
75 const char *envvar
= getenv("RESOLV_CONF");
82 fp
= fopen(resolvconf
, "r");
84 tevent_req_error(req
, errno
);
85 return tevent_req_post(req
, ev
);
89 ret
= parse_resolvconf_fp(
93 &state
->num_nameservers
);
95 if (resolv_conf_fp
== NULL
) {
100 tevent_req_error(req
, ret
);
101 return tevent_req_post(req
, ev
);
104 if (state
->num_nameservers
== 0) {
106 * glibc's getaddrinfo returns EAI_AGAIN when no
107 * nameservers are configured. EAGAIN seems closest.
109 tevent_req_error(req
, EAGAIN
);
110 return tevent_req_post(req
, ev
);
113 state
->dns_subreqs
= talloc_zero_array(
116 state
->num_nameservers
);
118 if (tevent_req_nomem(state
->dns_subreqs
, req
)) {
119 return tevent_req_post(req
, ev
);
122 ret
= dns_lookup_send_next(req
);
123 if (tevent_req_error(req
, ret
)) {
124 return tevent_req_post(req
, ev
);
130 static int dns_lookup_send_next(struct tevent_req
*req
)
132 struct dns_lookup_state
*state
= tevent_req_data(
133 req
, struct dns_lookup_state
);
135 DBG_DEBUG("Sending DNS request #%zu to %s\n",
137 state
->nameservers
[state
->num_sent
]);
139 state
->dns_subreqs
[state
->num_sent
] = dns_cli_request_send(
142 state
->nameservers
[state
->num_sent
],
147 if (state
->dns_subreqs
[state
->num_sent
] == NULL
) {
150 tevent_req_set_callback(state
->dns_subreqs
[state
->num_sent
],
153 state
->num_sent
+= 1;
155 if (state
->num_sent
== state
->num_nameservers
) {
157 * No more nameservers left
159 DBG_DEBUG("cancelling wait_subreq\n");
160 TALLOC_FREE(state
->wait_subreq
);
164 if (state
->wait_subreq
!= NULL
) {
166 * This can happen if we fire the next request upon
167 * dns_cli_request returning a network-level error
172 state
->wait_subreq
= tevent_wakeup_send(
175 tevent_timeval_current_ofs(1, 0));
176 if (state
->wait_subreq
== NULL
) {
179 tevent_req_set_callback(state
->wait_subreq
, dns_lookup_waited
, req
);
184 static void dns_lookup_done(struct tevent_req
*subreq
)
186 struct tevent_req
*req
= tevent_req_callback_data(
187 subreq
, struct tevent_req
);
188 struct dns_lookup_state
*state
= tevent_req_data(
189 req
, struct dns_lookup_state
);
190 int dns_cli_request_ret
;
193 dns_cli_request_ret
= dns_cli_request_recv(
198 for (i
= 0; i
< state
->num_nameservers
; i
++) {
199 if (state
->dns_subreqs
[i
] == subreq
) {
206 if (i
== state
->num_nameservers
) {
207 /* should never happen */
208 DBG_WARNING("Failed to find subreq\n");
209 tevent_req_error(req
, EINVAL
);
212 state
->dns_subreqs
[i
] = NULL
;
214 if (dns_cli_request_ret
== 0) {
216 * Success, cancel everything else
218 TALLOC_FREE(state
->dns_subreqs
);
219 TALLOC_FREE(state
->wait_subreq
);
220 tevent_req_done(req
);
224 DBG_DEBUG("dns_cli_request[%zu] returned %s\n", i
,
225 strerror(dns_cli_request_ret
));
227 if (state
->num_sent
< state
->num_nameservers
) {
229 * We have a nameserver left to try
233 ret
= dns_lookup_send_next(req
);
234 if (tevent_req_error(req
, ret
)) {
239 DBG_DEBUG("looking for outstanding requests\n");
241 for (i
= 0; i
<state
->num_nameservers
; i
++) {
242 if (state
->dns_subreqs
[i
] != NULL
) {
247 DBG_DEBUG("i=%zu, num_nameservers=%zu\n",
248 i
, state
->num_nameservers
);
250 if (i
== state
->num_nameservers
) {
252 * Report the lower-level error if we have nothing
253 * outstanding anymore
255 tevent_req_error(req
, dns_cli_request_ret
);
260 * Do nothing: We have other nameservers that might come back
261 * with something good.
265 static void dns_lookup_waited(struct tevent_req
*subreq
)
267 struct tevent_req
*req
= tevent_req_callback_data(
268 subreq
, struct tevent_req
);
269 struct dns_lookup_state
*state
= tevent_req_data(
270 req
, struct dns_lookup_state
);
274 DBG_DEBUG("waited\n");
276 ok
= tevent_wakeup_recv(subreq
);
282 state
->wait_subreq
= NULL
;
284 ret
= dns_lookup_send_next(req
);
285 if (tevent_req_error(req
, ret
)) {
290 * dns_lookup_send_next() has already triggered the next wakeup
294 int dns_lookup_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
295 struct dns_name_packet
**reply
)
297 struct dns_lookup_state
*state
= tevent_req_data(
298 req
, struct dns_lookup_state
);
301 if (tevent_req_is_unix_error(req
, &err
)) {
305 *reply
= talloc_move(mem_ctx
, &state
->reply
);
307 tevent_req_received(req
);
311 int dns_lookup(FILE *resolv_conf_fp
,
313 enum dns_qclass qclass
,
314 enum dns_qtype qtype
,
316 struct dns_name_packet
**reply
)
318 struct tevent_context
*ev
;
319 struct tevent_req
*req
;
322 ev
= samba_tevent_context_init(mem_ctx
);
326 req
= dns_lookup_send(ev
, ev
, resolv_conf_fp
, name
, qclass
, qtype
);
330 if (!tevent_req_poll_unix(req
, ev
, &ret
)) {
333 ret
= dns_lookup_recv(req
, mem_ctx
, reply
);
339 bool dns_res_rec_get_sockaddr(const struct dns_res_rec
*rec
,
340 struct sockaddr_storage
*addr
)
347 switch (rec
->rr_type
) {
350 src
= rec
->rdata
.ipv4_record
;
351 dst
= &(((struct sockaddr_in
*)addr
)->sin_addr
);
356 src
= rec
->rdata
.ipv6_record
;
357 dst
= &(((struct sockaddr_in6
*)addr
)->sin6_addr
);
361 /* We only care about IP addresses */
365 *addr
= (struct sockaddr_storage
) { .ss_family
= family
};
367 ret
= inet_pton(family
, src
, dst
);
369 DBG_DEBUG("inet_pton(%s) failed\n", src
);