libcli/dns: Add dns_res_rec_get_sockaddr
[Samba.git] / libcli / dns / dns_lookup.c
blob7402d443fba948cb16dd8c3fd7941f0e209b6c77
1 /*
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/>.
20 #include "replace.h"
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;
30 const char *name;
31 enum dns_qclass qclass;
32 enum dns_qtype qtype;
34 char **nameservers;
35 size_t num_nameservers;
36 size_t num_sent;
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,
51 FILE *resolv_conf_fp,
52 const char *name,
53 enum dns_qclass qclass,
54 enum dns_qtype qtype)
56 struct tevent_req *req;
57 struct dns_lookup_state *state;
58 FILE *fp = resolv_conf_fp;
59 int ret;
61 req = tevent_req_create(mem_ctx, &state, struct dns_lookup_state);
62 if (req == NULL) {
63 return NULL;
65 state->ev = ev;
66 state->name = name;
67 state->qclass = qclass;
68 state->qtype = qtype;
70 if (resolv_conf_fp == NULL) {
71 const char *resolvconf = "/etc/resolv.conf";
73 #ifdef DEVELOPER
75 const char *envvar = getenv("RESOLV_CONF");
76 if (envvar != NULL) {
77 resolvconf = envvar;
80 #endif
82 fp = fopen(resolvconf, "r");
83 if (fp == NULL) {
84 tevent_req_error(req, errno);
85 return tevent_req_post(req, ev);
89 ret = parse_resolvconf_fp(
90 fp,
91 state,
92 &state->nameservers,
93 &state->num_nameservers);
95 if (resolv_conf_fp == NULL) {
96 fclose(fp);
99 if (ret != 0) {
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(
114 state,
115 struct tevent_req *,
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);
127 return req;
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",
136 state->num_sent,
137 state->nameservers[state->num_sent]);
139 state->dns_subreqs[state->num_sent] = dns_cli_request_send(
140 state->dns_subreqs,
141 state->ev,
142 state->nameservers[state->num_sent],
143 state->name,
144 state->qclass,
145 state->qtype);
147 if (state->dns_subreqs[state->num_sent] == NULL) {
148 return ENOMEM;
150 tevent_req_set_callback(state->dns_subreqs[state->num_sent],
151 dns_lookup_done,
152 req);
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);
161 return 0;
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
169 return 0;
172 state->wait_subreq = tevent_wakeup_send(
173 state,
174 state->ev,
175 tevent_timeval_current_ofs(1, 0));
176 if (state->wait_subreq == NULL) {
177 return ENOMEM;
179 tevent_req_set_callback(state->wait_subreq, dns_lookup_waited, req);
181 return 0;
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;
191 size_t i;
193 dns_cli_request_ret = dns_cli_request_recv(
194 subreq,
195 state,
196 &state->reply);
198 for (i = 0; i < state->num_nameservers; i++) {
199 if (state->dns_subreqs[i] == subreq) {
200 break;
204 TALLOC_FREE(subreq);
206 if (i == state->num_nameservers) {
207 /* should never happen */
208 DBG_WARNING("Failed to find subreq");
209 tevent_req_error(req, EINVAL);
210 return;
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);
221 return;
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
231 int ret;
233 ret = dns_lookup_send_next(req);
234 if (tevent_req_error(req, ret)) {
235 return;
239 DBG_DEBUG("looking for outstanding requests\n");
241 for (i = 0; i<state->num_nameservers; i++) {
242 if (state->dns_subreqs[i] != NULL) {
243 break;
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);
256 return;
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);
271 int ret;
272 bool ok;
274 DBG_DEBUG("waited\n");
276 ok = tevent_wakeup_recv(subreq);
277 TALLOC_FREE(subreq);
278 if (!ok) {
279 tevent_req_oom(req);
280 return;
282 state->wait_subreq = NULL;
284 ret = dns_lookup_send_next(req);
285 if (tevent_req_error(req, ret)) {
286 return;
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);
299 int err;
301 if (tevent_req_is_unix_error(req, &err)) {
302 return err;
305 *reply = talloc_move(mem_ctx, &state->reply);
307 tevent_req_received(req);
308 return 0;
311 int dns_lookup(FILE *resolv_conf_fp,
312 const char *name,
313 enum dns_qclass qclass,
314 enum dns_qtype qtype,
315 TALLOC_CTX *mem_ctx,
316 struct dns_name_packet **reply)
318 struct tevent_context *ev;
319 struct tevent_req *req;
320 int ret = ENOMEM;
322 ev = samba_tevent_context_init(mem_ctx);
323 if (ev == NULL) {
324 goto fail;
326 req = dns_lookup_send(ev, ev, resolv_conf_fp, name, qclass, qtype);
327 if (req == NULL) {
328 goto fail;
330 if (!tevent_req_poll_unix(req, ev, &ret)) {
331 goto fail;
333 ret = dns_lookup_recv(req, mem_ctx, reply);
334 fail:
335 TALLOC_FREE(ev);
336 return ret;
339 bool dns_res_rec_get_sockaddr(const struct dns_res_rec *rec,
340 struct sockaddr_storage *addr)
342 sa_family_t family;
343 const char *src;
344 void *dst;
345 int ret;
347 switch (rec->rr_type) {
348 case DNS_QTYPE_A:
349 family = AF_INET;
350 src = rec->rdata.ipv4_record;
351 dst = &(((struct sockaddr_in *)addr)->sin_addr);
352 break;
353 #ifdef HAVE_IPV6
354 case DNS_QTYPE_AAAA:
355 family = AF_INET6;
356 src = rec->rdata.ipv6_record;
357 dst = &(((struct sockaddr_in6 *)addr)->sin6_addr);
358 break;
359 #endif
360 default:
361 /* We only care about IP addresses */
362 return false;
365 *addr = (struct sockaddr_storage) { .ss_family = family };
367 ret = inet_pton(family, src, dst);
368 if (ret != 1) {
369 DBG_DEBUG("inet_pton(%s) failed\n", src);
370 return false;
373 return true;