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 fp = fopen("/etc/resolv.conf", "r");
73 tevent_req_error(req, errno);
74 return tevent_req_post(req, ev);
78 ret = parse_resolvconf_fp(
82 &state->num_nameservers);
84 if (resolv_conf_fp == NULL) {
89 tevent_req_error(req, ret);
90 return tevent_req_post(req, ev);
93 if (state->num_nameservers == 0) {
95 * glibc's getaddrinfo returns EAI_AGAIN when no
96 * nameservers are configured. EAGAIN seems closest.
98 tevent_req_error(req, EAGAIN);
99 return tevent_req_post(req, ev);
102 state->dns_subreqs = talloc_zero_array(
105 state->num_nameservers);
107 if (tevent_req_nomem(state->dns_subreqs, req)) {
108 return tevent_req_post(req, ev);
111 ret = dns_lookup_send_next(req);
112 if (tevent_req_error(req, ret)) {
113 return tevent_req_post(req, ev);
119 static int dns_lookup_send_next(struct tevent_req *req)
121 struct dns_lookup_state *state = tevent_req_data(
122 req, struct dns_lookup_state);
124 DBG_DEBUG("Sending DNS request #%zu to %s\n",
126 state->nameservers[state->num_sent]);
128 state->dns_subreqs[state->num_sent] = dns_cli_request_send(
131 state->nameservers[state->num_sent],
136 if (state->dns_subreqs[state->num_sent] == NULL) {
139 tevent_req_set_callback(state->dns_subreqs[state->num_sent],
142 state->num_sent += 1;
144 if (state->num_sent == state->num_nameservers) {
146 * No more nameservers left
148 DBG_DEBUG("cancelling wait_subreq\n");
149 TALLOC_FREE(state->wait_subreq);
153 if (state->wait_subreq != NULL) {
155 * This can happen if we fire the next request upon
156 * dns_cli_request returning a network-level error
161 state->wait_subreq = tevent_wakeup_send(
164 tevent_timeval_current_ofs(1, 0));
165 if (state->wait_subreq == NULL) {
168 tevent_req_set_callback(state->wait_subreq, dns_lookup_waited, req);
173 static void dns_lookup_done(struct tevent_req *subreq)
175 struct tevent_req *req = tevent_req_callback_data(
176 subreq, struct tevent_req);
177 struct dns_lookup_state *state = tevent_req_data(
178 req, struct dns_lookup_state);
179 int dns_cli_request_ret;
182 dns_cli_request_ret = dns_cli_request_recv(
187 for (i = 0; i < state->num_nameservers; i++) {
188 if (state->dns_subreqs[i] == subreq) {
195 if (i == state->num_nameservers) {
196 /* should never happen */
197 DBG_WARNING("Failed to find subreq");
198 tevent_req_error(req, EINVAL);
201 state->dns_subreqs[i] = NULL;
203 if (dns_cli_request_ret == 0) {
205 * Success, cancel everything else
207 TALLOC_FREE(state->dns_subreqs);
208 TALLOC_FREE(state->wait_subreq);
209 tevent_req_done(req);
213 DBG_DEBUG("dns_cli_request[%zu] returned %s\n", i,
214 strerror(dns_cli_request_ret));
216 if (state->num_sent < state->num_nameservers) {
218 * We have a nameserver left to try
222 ret = dns_lookup_send_next(req);
223 if (tevent_req_error(req, ret)) {
228 DBG_DEBUG("looking for outstanding requests\n");
230 for (i = 0; i<state->num_nameservers; i++) {
231 if (state->dns_subreqs[i] != NULL) {
236 DBG_DEBUG("i=%zu, num_nameservers=%zu\n",
237 i, state->num_nameservers);
239 if (i == state->num_nameservers) {
241 * Report the lower-level error if we have nothing
242 * outstanding anymore
244 tevent_req_error(req, dns_cli_request_ret);
249 * Do nothing: We have other nameservers that might come back
250 * with something good.
254 static void dns_lookup_waited(struct tevent_req *subreq)
256 struct tevent_req *req = tevent_req_callback_data(
257 subreq, struct tevent_req);
258 struct dns_lookup_state *state = tevent_req_data(
259 req, struct dns_lookup_state);
263 DBG_DEBUG("waited\n");
265 ok = tevent_wakeup_recv(subreq);
271 state->wait_subreq = NULL;
273 ret = dns_lookup_send_next(req);
274 if (tevent_req_error(req, ret)) {
279 * dns_lookup_send_next() has already triggered the next wakeup
283 int dns_lookup_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
284 struct dns_name_packet **reply)
286 struct dns_lookup_state *state = tevent_req_data(
287 req, struct dns_lookup_state);
290 if (tevent_req_is_unix_error(req, &err)) {
294 *reply = talloc_move(mem_ctx, &state->reply);
296 tevent_req_received(req);
300 int dns_lookup(FILE *resolv_conf_fp,
302 enum dns_qclass qclass,
303 enum dns_qtype qtype,
305 struct dns_name_packet **reply)
307 struct tevent_context *ev;
308 struct tevent_req *req;
311 ev = samba_tevent_context_init(mem_ctx);
315 req = dns_lookup_send(ev, ev, resolv_conf_fp, name, qclass, qtype);
319 if (!tevent_req_poll_unix(req, ev, &ret)) {
322 ret = dns_lookup_recv(req, mem_ctx, reply);