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");
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);