s3: Fix possible mem leak
[Samba.git] / libcli / dns / dns.c
blobc30de2d4addfc1eb3af473914ee74a62e65209c6
1 /*
2 Unix SMB/CIFS implementation.
4 Small async DNS library for Samba with socketwrapper support
6 Copyright (C) 2010 Kai Blin <kai@samba.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "replace.h"
23 #include "system/network.h"
24 #include <tevent.h>
25 #include "lib/tsocket/tsocket.h"
26 #include "libcli/dns/libdns.h"
27 #include "lib/util/tevent_unix.h"
28 #include "lib/util/samba_util.h"
29 #include "lib/util/debug.h"
30 #include "libcli/util/error.h"
31 #include "librpc/ndr/libndr.h"
32 #include "librpc/gen_ndr/ndr_dns.h"
34 struct dns_udp_request_state {
35 struct tevent_context *ev;
36 struct tdgram_context *dgram;
37 size_t query_len;
38 uint8_t *reply;
39 size_t reply_len;
42 #define DNS_REQUEST_TIMEOUT 2
44 /* Declare callback functions used below. */
45 static void dns_udp_request_get_reply(struct tevent_req *subreq);
46 static void dns_udp_request_done(struct tevent_req *subreq);
48 static struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
49 struct tevent_context *ev,
50 const char *server_addr_string,
51 const uint8_t *query,
52 size_t query_len)
54 struct tevent_req *req, *subreq;
55 struct dns_udp_request_state *state;
56 struct tsocket_address *local_addr, *server_addr;
57 struct tdgram_context *dgram;
58 int ret;
60 req = tevent_req_create(mem_ctx, &state, struct dns_udp_request_state);
61 if (req == NULL) {
62 return NULL;
65 state->ev = ev;
67 /* Use connected UDP sockets */
68 ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
69 &local_addr);
70 if (ret != 0) {
71 tevent_req_error(req, errno);
72 return tevent_req_post(req, ev);
75 ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
76 DNS_SERVICE_PORT, &server_addr);
77 if (ret != 0) {
78 tevent_req_error(req, errno);
79 return tevent_req_post(req, ev);
82 ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
83 if (ret != 0) {
84 tevent_req_error(req, errno);
85 return tevent_req_post(req, ev);
88 state->dgram = dgram;
89 state->query_len = query_len;
91 dump_data(10, query, query_len);
93 subreq = tdgram_sendto_send(state, ev, dgram, query, query_len, NULL);
94 if (tevent_req_nomem(subreq, req)) {
95 return tevent_req_post(req, ev);
98 if (!tevent_req_set_endtime(req, ev,
99 timeval_current_ofs(DNS_REQUEST_TIMEOUT, 0))) {
100 tevent_req_oom(req);
101 return tevent_req_post(req, ev);
104 tevent_req_set_callback(subreq, dns_udp_request_get_reply, req);
105 return req;
108 static void dns_udp_request_get_reply(struct tevent_req *subreq)
110 struct tevent_req *req = tevent_req_callback_data(subreq,
111 struct tevent_req);
112 struct dns_udp_request_state *state = tevent_req_data(req,
113 struct dns_udp_request_state);
114 ssize_t len;
115 int err = 0;
117 len = tdgram_sendto_recv(subreq, &err);
118 TALLOC_FREE(subreq);
120 if (len == -1 && err != 0) {
121 tevent_req_error(req, err);
122 return;
125 if (len != state->query_len) {
126 tevent_req_error(req, EIO);
127 return;
130 subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
131 if (tevent_req_nomem(subreq, req)) {
132 return;
135 tevent_req_set_callback(subreq, dns_udp_request_done, req);
138 static void dns_udp_request_done(struct tevent_req *subreq)
140 struct tevent_req *req = tevent_req_callback_data(subreq,
141 struct tevent_req);
142 struct dns_udp_request_state *state = tevent_req_data(req,
143 struct dns_udp_request_state);
145 ssize_t len;
146 int err = 0;
148 len = tdgram_recvfrom_recv(subreq, &err, state, &state->reply, NULL);
149 TALLOC_FREE(subreq);
151 if (len == -1 && err != 0) {
152 tevent_req_error(req, err);
153 return;
156 state->reply_len = len;
157 dump_data(10, state->reply, state->reply_len);
158 tevent_req_done(req);
161 static int dns_udp_request_recv(struct tevent_req *req,
162 TALLOC_CTX *mem_ctx,
163 uint8_t **reply,
164 size_t *reply_len)
166 struct dns_udp_request_state *state = tevent_req_data(req,
167 struct dns_udp_request_state);
168 int err;
170 if (tevent_req_is_unix_error(req, &err)) {
171 tevent_req_received(req);
172 return err;
175 *reply = talloc_move(mem_ctx, &state->reply);
176 *reply_len = state->reply_len;
177 tevent_req_received(req);
179 return 0;
182 struct dns_tcp_request_state {
183 struct tevent_context *ev;
184 struct tstream_context *stream;
185 const uint8_t *query;
186 size_t query_len;
188 uint8_t dns_msglen_hdr[2];
189 struct iovec iov[2];
191 size_t nread;
192 uint8_t *reply;
195 static void dns_tcp_request_connected(struct tevent_req *subreq);
196 static void dns_tcp_request_sent(struct tevent_req *subreq);
197 static int dns_tcp_request_next_vector(struct tstream_context *stream,
198 void *private_data,
199 TALLOC_CTX *mem_ctx,
200 struct iovec **_vector,
201 size_t *_count);
202 static void dns_tcp_request_received(struct tevent_req *subreq);
204 static struct tevent_req *dns_tcp_request_send(TALLOC_CTX *mem_ctx,
205 struct tevent_context *ev,
206 const char *server_addr_string,
207 const uint8_t *query,
208 size_t query_len)
210 struct tevent_req *req, *subreq;
211 struct dns_tcp_request_state *state;
212 struct tsocket_address *local, *remote;
213 int ret;
215 req = tevent_req_create(mem_ctx, &state,
216 struct dns_tcp_request_state);
217 if (req == NULL) {
218 return NULL;
220 state->ev = ev;
221 state->query = query;
222 state->query_len = query_len;
224 if (query_len > UINT16_MAX) {
225 tevent_req_error(req, EMSGSIZE);
226 return tevent_req_post(req, ev);
229 ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local);
230 if (ret != 0) {
231 tevent_req_error(req, errno);
232 return tevent_req_post(req, ev);
235 ret = tsocket_address_inet_from_strings(
236 state, "ip", server_addr_string, DNS_SERVICE_PORT, &remote);
237 if (ret != 0) {
238 tevent_req_error(req, errno);
239 return tevent_req_post(req, ev);
242 subreq = tstream_inet_tcp_connect_send(state, state->ev,
243 local, remote);
244 if (tevent_req_nomem(subreq, req)) {
245 return tevent_req_post(req, ev);
247 tevent_req_set_callback(subreq, dns_tcp_request_connected, req);
249 return req;
252 static void dns_tcp_request_connected(struct tevent_req *subreq)
254 struct tevent_req *req = tevent_req_callback_data(
255 subreq, struct tevent_req);
256 struct dns_tcp_request_state *state = tevent_req_data(
257 req, struct dns_tcp_request_state);
258 int ret, err;
260 ret = tstream_inet_tcp_connect_recv(subreq, &err, state,
261 &state->stream, NULL);
262 TALLOC_FREE(subreq);
263 if (ret == -1) {
264 tevent_req_error(req, err);
265 return;
268 RSSVAL(state->dns_msglen_hdr, 0, state->query_len);
269 state->iov[0] = (struct iovec) {
270 .iov_base = state->dns_msglen_hdr,
271 .iov_len = sizeof(state->dns_msglen_hdr)
273 state->iov[1] = (struct iovec) {
274 .iov_base = discard_const_p(void, state->query),
275 .iov_len = state->query_len
278 subreq = tstream_writev_send(state, state->ev, state->stream,
279 state->iov, ARRAY_SIZE(state->iov));
280 if (tevent_req_nomem(subreq, req)) {
281 return;
283 tevent_req_set_callback(subreq, dns_tcp_request_sent, req);
286 static void dns_tcp_request_sent(struct tevent_req *subreq)
288 struct tevent_req *req = tevent_req_callback_data(
289 subreq, struct tevent_req);
290 struct dns_tcp_request_state *state = tevent_req_data(
291 req, struct dns_tcp_request_state);
292 int ret, err;
294 ret = tstream_writev_recv(subreq, &err);
295 TALLOC_FREE(subreq);
296 if (ret == -1) {
297 tevent_req_error(req, err);
298 return;
301 subreq = tstream_readv_pdu_send(state, state->ev, state->stream,
302 dns_tcp_request_next_vector, state);
303 if (tevent_req_nomem(subreq, req)) {
304 return;
306 tevent_req_set_callback(subreq, dns_tcp_request_received, req);
309 static int dns_tcp_request_next_vector(struct tstream_context *stream,
310 void *private_data,
311 TALLOC_CTX *mem_ctx,
312 struct iovec **_vector,
313 size_t *_count)
315 struct dns_tcp_request_state *state = talloc_get_type_abort(
316 private_data, struct dns_tcp_request_state);
317 struct iovec *vector;
318 uint16_t msglen;
320 if (state->nread == 0) {
321 vector = talloc_array(mem_ctx, struct iovec, 1);
322 if (vector == NULL) {
323 return -1;
325 vector[0] = (struct iovec) {
326 .iov_base = state->dns_msglen_hdr,
327 .iov_len = sizeof(state->dns_msglen_hdr)
329 state->nread = sizeof(state->dns_msglen_hdr);
331 *_vector = vector;
332 *_count = 1;
333 return 0;
336 if (state->nread == sizeof(state->dns_msglen_hdr)) {
337 msglen = RSVAL(state->dns_msglen_hdr, 0);
339 state->reply = talloc_array(state, uint8_t, msglen);
340 if (state->reply == NULL) {
341 return -1;
344 vector = talloc_array(mem_ctx, struct iovec, 1);
345 if (vector == NULL) {
346 return -1;
348 vector[0] = (struct iovec) {
349 .iov_base = state->reply,
350 .iov_len = msglen
352 state->nread += msglen;
354 *_vector = vector;
355 *_count = 1;
356 return 0;
359 *_vector = NULL;
360 *_count = 0;
361 return 0;
364 static void dns_tcp_request_received(struct tevent_req *subreq)
366 struct tevent_req *req = tevent_req_callback_data(
367 subreq, struct tevent_req);
368 int ret, err;
370 ret = tstream_readv_pdu_recv(subreq, &err);
371 TALLOC_FREE(subreq);
372 if (ret == -1) {
373 tevent_req_error(req, err);
374 return;
377 tevent_req_done(req);
380 static int dns_tcp_request_recv(struct tevent_req *req,
381 TALLOC_CTX *mem_ctx,
382 uint8_t **reply,
383 size_t *reply_len)
385 struct dns_tcp_request_state *state = tevent_req_data(
386 req, struct dns_tcp_request_state);
387 int err;
389 if (tevent_req_is_unix_error(req, &err)) {
390 tevent_req_received(req);
391 return err;
394 *reply_len = talloc_array_length(state->reply);
395 *reply = talloc_move(mem_ctx, &state->reply);
396 tevent_req_received(req);
398 return 0;
401 struct dns_cli_request_state {
402 struct tevent_context *ev;
403 const char *nameserver;
405 uint16_t req_id;
407 DATA_BLOB query;
409 struct dns_name_packet *reply;
412 static void dns_cli_request_udp_done(struct tevent_req *subreq);
413 static void dns_cli_request_tcp_done(struct tevent_req *subreq);
415 struct tevent_req *dns_cli_request_send(TALLOC_CTX *mem_ctx,
416 struct tevent_context *ev,
417 const char *nameserver,
418 const char *name,
419 enum dns_qclass qclass,
420 enum dns_qtype qtype)
422 struct tevent_req *req, *subreq;
423 struct dns_cli_request_state *state;
424 struct dns_name_question question;
425 struct dns_name_packet out_packet;
426 enum ndr_err_code ndr_err;
428 req = tevent_req_create(mem_ctx, &state,
429 struct dns_cli_request_state);
430 if (req == NULL) {
431 return NULL;
433 state->ev = ev;
434 state->nameserver = nameserver;
436 DBG_DEBUG("Asking %s for %s/%d/%d via UDP\n", nameserver,
437 name, (int)qclass, (int)qtype);
439 generate_random_buffer((uint8_t *)&state->req_id,
440 sizeof(state->req_id));
442 question = (struct dns_name_question) {
443 .name = discard_const_p(char, name),
444 .question_type = qtype, .question_class = qclass
447 out_packet = (struct dns_name_packet) {
448 .id = state->req_id,
449 .operation = DNS_OPCODE_QUERY | DNS_FLAG_RECURSION_DESIRED,
450 .qdcount = 1,
451 .questions = &question
454 ndr_err = ndr_push_struct_blob(
455 &state->query, state, &out_packet,
456 (ndr_push_flags_fn_t)ndr_push_dns_name_packet);
457 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
458 tevent_req_error(req, ndr_map_error2errno(ndr_err));
459 return tevent_req_post(req, ev);
462 subreq = dns_udp_request_send(state, state->ev, state->nameserver,
463 state->query.data, state->query.length);
464 if (tevent_req_nomem(subreq, req)) {
465 return tevent_req_post(req, ev);
467 tevent_req_set_callback(subreq, dns_cli_request_udp_done, req);
468 return req;
471 static void dns_cli_request_udp_done(struct tevent_req *subreq)
473 struct tevent_req *req = tevent_req_callback_data(
474 subreq, struct tevent_req);
475 struct dns_cli_request_state *state = tevent_req_data(
476 req, struct dns_cli_request_state);
477 DATA_BLOB reply;
478 enum ndr_err_code ndr_err;
479 int ret;
481 ret = dns_udp_request_recv(subreq, state, &reply.data, &reply.length);
482 TALLOC_FREE(subreq);
483 if (tevent_req_error(req, ret)) {
484 return;
487 state->reply = talloc(state, struct dns_name_packet);
488 if (tevent_req_nomem(state->reply, req)) {
489 return;
492 ndr_err = ndr_pull_struct_blob(
493 &reply, state->reply, state->reply,
494 (ndr_pull_flags_fn_t)ndr_pull_dns_name_packet);
495 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
496 tevent_req_error(req, ndr_map_error2errno(ndr_err));
497 return;
499 TALLOC_FREE(reply.data);
501 if (state->reply->id != state->req_id) {
502 DBG_DEBUG("Got id %"PRIu16", expected %"PRIu16"\n",
503 state->reply->id, state->req_id);
504 tevent_req_error(req, ENOMSG);
505 return;
508 if ((state->reply->operation & DNS_FLAG_TRUNCATION) == 0) {
509 DBG_DEBUG("Got op=%x %"PRIu16"/%"PRIu16"/%"PRIu16"/%"PRIu16
510 " recs\n", (int)state->reply->operation,
511 state->reply->qdcount, state->reply->ancount,
512 state->reply->nscount, state->reply->nscount);
513 tevent_req_done(req);
514 return;
517 DBG_DEBUG("Reply was truncated, retrying TCP\n");
519 TALLOC_FREE(state->reply);
521 subreq = dns_tcp_request_send(state, state->ev, state->nameserver,
522 state->query.data, state->query.length);
523 if (tevent_req_nomem(subreq, req)) {
524 return;
526 tevent_req_set_callback(subreq, dns_cli_request_tcp_done, req);
529 static void dns_cli_request_tcp_done(struct tevent_req *subreq)
531 struct tevent_req *req = tevent_req_callback_data(
532 subreq, struct tevent_req);
533 struct dns_cli_request_state *state = tevent_req_data(
534 req, struct dns_cli_request_state);
535 DATA_BLOB reply;
536 enum ndr_err_code ndr_err;
537 int ret;
539 ret = dns_tcp_request_recv(subreq, state, &reply.data, &reply.length);
540 TALLOC_FREE(subreq);
541 if (tevent_req_error(req, ret)) {
542 return;
545 state->reply = talloc(state, struct dns_name_packet);
546 if (tevent_req_nomem(state->reply, req)) {
547 return;
550 ndr_err = ndr_pull_struct_blob(
551 &reply, state->reply, state->reply,
552 (ndr_pull_flags_fn_t)ndr_pull_dns_name_packet);
553 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
554 tevent_req_error(req, ndr_map_error2errno(ndr_err));
555 return;
557 TALLOC_FREE(reply.data);
559 if (state->reply->id != state->req_id) {
560 DBG_DEBUG("Got id %"PRIu16", expected %"PRIu16"\n",
561 state->reply->id, state->req_id);
562 tevent_req_error(req, ENOMSG);
563 return;
566 DBG_DEBUG("Got op=%x %"PRIu16"/%"PRIu16"/%"PRIu16"/%"PRIu16
567 " recs\n", (int)state->reply->operation,
568 state->reply->qdcount, state->reply->ancount,
569 state->reply->nscount, state->reply->nscount);
571 tevent_req_done(req);
574 int dns_cli_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
575 struct dns_name_packet **reply)
577 struct dns_cli_request_state *state = tevent_req_data(
578 req, struct dns_cli_request_state);
579 int err;
581 if (tevent_req_is_unix_error(req, &err)) {
582 return err;
584 *reply = talloc_move(mem_ctx, &state->reply);
585 return 0;