Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : * Internal DNS query structures
4 : * Copyright (C) Volker Lendecke 2018
5 : *
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.
10 : *
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.
15 : *
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/>.
18 : */
19 :
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"
27 :
28 : struct dns_lookup_state {
29 : struct tevent_context *ev;
30 : const char *name;
31 : enum dns_qclass qclass;
32 : enum dns_qtype qtype;
33 :
34 : char **nameservers;
35 : size_t num_nameservers;
36 : size_t num_sent;
37 :
38 : struct tevent_req **dns_subreqs;
39 : struct tevent_req *wait_subreq;
40 :
41 : struct dns_name_packet *reply;
42 : };
43 :
44 : static int dns_lookup_send_next(struct tevent_req *req);
45 :
46 : static void dns_lookup_done(struct tevent_req *subreq);
47 : static void dns_lookup_waited(struct tevent_req *subreq);
48 :
49 1424 : 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)
55 : {
56 0 : struct tevent_req *req;
57 0 : struct dns_lookup_state *state;
58 1424 : FILE *fp = resolv_conf_fp;
59 0 : int ret;
60 :
61 1424 : req = tevent_req_create(mem_ctx, &state, struct dns_lookup_state);
62 1424 : if (req == NULL) {
63 0 : return NULL;
64 : }
65 1424 : state->ev = ev;
66 1424 : state->name = name;
67 1424 : state->qclass = qclass;
68 1424 : state->qtype = qtype;
69 :
70 1424 : if (resolv_conf_fp == NULL) {
71 1424 : const char *resolvconf = "/etc/resolv.conf";
72 :
73 : #ifdef ENABLE_SELFTEST
74 : {
75 1424 : const char *envvar = getenv("RESOLV_CONF");
76 1424 : if (envvar != NULL) {
77 1424 : resolvconf = envvar;
78 : }
79 : }
80 : #endif
81 :
82 1424 : fp = fopen(resolvconf, "r");
83 1424 : if (fp == NULL) {
84 13 : tevent_req_error(req, errno);
85 13 : return tevent_req_post(req, ev);
86 : }
87 : }
88 :
89 1411 : ret = parse_resolvconf_fp(
90 : fp,
91 : state,
92 1411 : &state->nameservers,
93 1411 : &state->num_nameservers);
94 :
95 1411 : if (resolv_conf_fp == NULL) {
96 1411 : fclose(fp);
97 : }
98 :
99 1411 : if (ret != 0) {
100 0 : tevent_req_error(req, ret);
101 0 : return tevent_req_post(req, ev);
102 : }
103 :
104 1411 : if (state->num_nameservers == 0) {
105 : /*
106 : * glibc's getaddrinfo returns EAI_AGAIN when no
107 : * nameservers are configured. EAGAIN seems closest.
108 : */
109 0 : tevent_req_error(req, EAGAIN);
110 0 : return tevent_req_post(req, ev);
111 : }
112 :
113 1411 : state->dns_subreqs = talloc_zero_array(
114 : state,
115 : struct tevent_req *,
116 : state->num_nameservers);
117 :
118 1411 : if (tevent_req_nomem(state->dns_subreqs, req)) {
119 0 : return tevent_req_post(req, ev);
120 : }
121 :
122 1411 : ret = dns_lookup_send_next(req);
123 1411 : if (tevent_req_error(req, ret)) {
124 0 : return tevent_req_post(req, ev);
125 : }
126 :
127 1411 : return req;
128 : }
129 :
130 1411 : static int dns_lookup_send_next(struct tevent_req *req)
131 : {
132 1411 : struct dns_lookup_state *state = tevent_req_data(
133 : req, struct dns_lookup_state);
134 :
135 1411 : DBG_DEBUG("Sending DNS request #%zu to %s\n",
136 : state->num_sent,
137 : state->nameservers[state->num_sent]);
138 :
139 2822 : state->dns_subreqs[state->num_sent] = dns_cli_request_send(
140 1411 : state->dns_subreqs,
141 : state->ev,
142 1411 : state->nameservers[state->num_sent],
143 : state->name,
144 : state->qclass,
145 : state->qtype);
146 :
147 1411 : if (state->dns_subreqs[state->num_sent] == NULL) {
148 0 : return ENOMEM;
149 : }
150 1411 : tevent_req_set_callback(state->dns_subreqs[state->num_sent],
151 : dns_lookup_done,
152 : req);
153 1411 : state->num_sent += 1;
154 :
155 1411 : if (state->num_sent == state->num_nameservers) {
156 : /*
157 : * No more nameservers left
158 : */
159 0 : DBG_DEBUG("cancelling wait_subreq\n");
160 0 : TALLOC_FREE(state->wait_subreq);
161 0 : return 0;
162 : }
163 :
164 1411 : if (state->wait_subreq != NULL) {
165 : /*
166 : * This can happen if we fire the next request upon
167 : * dns_cli_request returning a network-level error
168 : */
169 0 : return 0;
170 : }
171 :
172 1411 : state->wait_subreq = tevent_wakeup_send(
173 : state,
174 : state->ev,
175 : tevent_timeval_current_ofs(1, 0));
176 1411 : if (state->wait_subreq == NULL) {
177 0 : return ENOMEM;
178 : }
179 1411 : tevent_req_set_callback(state->wait_subreq, dns_lookup_waited, req);
180 :
181 1411 : return 0;
182 : }
183 :
184 1411 : static void dns_lookup_done(struct tevent_req *subreq)
185 : {
186 1411 : struct tevent_req *req = tevent_req_callback_data(
187 : subreq, struct tevent_req);
188 1411 : struct dns_lookup_state *state = tevent_req_data(
189 : req, struct dns_lookup_state);
190 0 : int dns_cli_request_ret;
191 0 : size_t i;
192 :
193 1411 : dns_cli_request_ret = dns_cli_request_recv(
194 : subreq,
195 : state,
196 : &state->reply);
197 :
198 1411 : for (i = 0; i < state->num_nameservers; i++) {
199 1411 : if (state->dns_subreqs[i] == subreq) {
200 1411 : break;
201 : }
202 : }
203 :
204 1411 : TALLOC_FREE(subreq);
205 :
206 1411 : if (i == state->num_nameservers) {
207 : /* should never happen */
208 0 : DBG_WARNING("Failed to find subreq\n");
209 0 : tevent_req_error(req, EINVAL);
210 0 : return;
211 : }
212 1411 : state->dns_subreqs[i] = NULL;
213 :
214 1411 : if (dns_cli_request_ret == 0) {
215 : /*
216 : * Success, cancel everything else
217 : */
218 1411 : TALLOC_FREE(state->dns_subreqs);
219 1411 : TALLOC_FREE(state->wait_subreq);
220 1411 : tevent_req_done(req);
221 1411 : return;
222 : }
223 :
224 0 : DBG_DEBUG("dns_cli_request[%zu] returned %s\n", i,
225 : strerror(dns_cli_request_ret));
226 :
227 0 : if (state->num_sent < state->num_nameservers) {
228 : /*
229 : * We have a nameserver left to try
230 : */
231 0 : int ret;
232 :
233 0 : ret = dns_lookup_send_next(req);
234 0 : if (tevent_req_error(req, ret)) {
235 0 : return;
236 : }
237 : }
238 :
239 0 : DBG_DEBUG("looking for outstanding requests\n");
240 :
241 0 : for (i = 0; i<state->num_nameservers; i++) {
242 0 : if (state->dns_subreqs[i] != NULL) {
243 0 : break;
244 : }
245 : }
246 :
247 0 : DBG_DEBUG("i=%zu, num_nameservers=%zu\n",
248 : i, state->num_nameservers);
249 :
250 0 : if (i == state->num_nameservers) {
251 : /*
252 : * Report the lower-level error if we have nothing
253 : * outstanding anymore
254 : */
255 0 : tevent_req_error(req, dns_cli_request_ret);
256 0 : return;
257 : }
258 :
259 : /*
260 : * Do nothing: We have other nameservers that might come back
261 : * with something good.
262 : */
263 : }
264 :
265 0 : static void dns_lookup_waited(struct tevent_req *subreq)
266 : {
267 0 : struct tevent_req *req = tevent_req_callback_data(
268 : subreq, struct tevent_req);
269 0 : struct dns_lookup_state *state = tevent_req_data(
270 : req, struct dns_lookup_state);
271 0 : int ret;
272 0 : bool ok;
273 :
274 0 : DBG_DEBUG("waited\n");
275 :
276 0 : ok = tevent_wakeup_recv(subreq);
277 0 : TALLOC_FREE(subreq);
278 0 : if (!ok) {
279 0 : tevent_req_oom(req);
280 0 : return;
281 : }
282 0 : state->wait_subreq = NULL;
283 :
284 0 : ret = dns_lookup_send_next(req);
285 0 : if (tevent_req_error(req, ret)) {
286 0 : return;
287 : }
288 :
289 : /*
290 : * dns_lookup_send_next() has already triggered the next wakeup
291 : */
292 : }
293 :
294 1424 : int dns_lookup_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
295 : struct dns_name_packet **reply)
296 : {
297 1424 : struct dns_lookup_state *state = tevent_req_data(
298 : req, struct dns_lookup_state);
299 0 : int err;
300 :
301 1424 : if (tevent_req_is_unix_error(req, &err)) {
302 13 : return err;
303 : }
304 :
305 1411 : *reply = talloc_move(mem_ctx, &state->reply);
306 :
307 1411 : tevent_req_received(req);
308 1411 : return 0;
309 : }
310 :
311 0 : 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)
317 : {
318 0 : struct tevent_context *ev;
319 0 : struct tevent_req *req;
320 0 : int ret = ENOMEM;
321 :
322 0 : ev = samba_tevent_context_init(mem_ctx);
323 0 : if (ev == NULL) {
324 0 : goto fail;
325 : }
326 0 : req = dns_lookup_send(ev, ev, resolv_conf_fp, name, qclass, qtype);
327 0 : if (req == NULL) {
328 0 : goto fail;
329 : }
330 0 : if (!tevent_req_poll_unix(req, ev, &ret)) {
331 0 : goto fail;
332 : }
333 0 : ret = dns_lookup_recv(req, mem_ctx, reply);
334 0 : fail:
335 0 : TALLOC_FREE(ev);
336 0 : return ret;
337 : }
338 :
339 892 : bool dns_res_rec_get_sockaddr(const struct dns_res_rec *rec,
340 : struct sockaddr_storage *addr)
341 : {
342 0 : sa_family_t family;
343 0 : const char *src;
344 0 : void *dst;
345 0 : int ret;
346 :
347 892 : switch (rec->rr_type) {
348 447 : case DNS_QTYPE_A:
349 447 : family = AF_INET;
350 447 : src = rec->rdata.ipv4_record;
351 447 : dst = &(((struct sockaddr_in *)addr)->sin_addr);
352 447 : break;
353 : #ifdef HAVE_IPV6
354 445 : case DNS_QTYPE_AAAA:
355 445 : family = AF_INET6;
356 445 : src = rec->rdata.ipv6_record;
357 445 : dst = &(((struct sockaddr_in6 *)addr)->sin6_addr);
358 445 : break;
359 : #endif
360 0 : default:
361 : /* We only care about IP addresses */
362 0 : return false;
363 : }
364 :
365 892 : *addr = (struct sockaddr_storage) { .ss_family = family };
366 :
367 892 : ret = inet_pton(family, src, dst);
368 892 : if (ret != 1) {
369 0 : DBG_DEBUG("inet_pton(%s) failed\n", src);
370 0 : return false;
371 : }
372 :
373 892 : return true;
374 : }
|