From 0bb92d7f377caffb2425cc7757b370bcf671e598 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 29 Dec 2017 11:01:29 +0100 Subject: libdns: Add dns_cli_request First UDP, then TCP if truncation happened Signed-off-by: Volker Lendecke --- libcli/dns/dns.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++- libcli/dns/libdns.h | 18 +++++ libcli/dns/wscript_build | 2 +- 3 files changed, 209 insertions(+), 2 deletions(-) (limited to 'libcli') diff --git a/libcli/dns/dns.c b/libcli/dns/dns.c index 9e180fe3fc9..6404cb8aa40 100644 --- a/libcli/dns/dns.c +++ b/libcli/dns/dns.c @@ -26,8 +26,10 @@ #include "libcli/dns/libdns.h" #include "lib/util/tevent_unix.h" #include "lib/util/samba_util.h" +#include "lib/util/debug.h" #include "libcli/util/error.h" -#include "librpc/gen_ndr/dns.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_dns.h" struct dns_udp_request_state { struct tevent_context *ev; @@ -395,3 +397,190 @@ int dns_tcp_request_recv(struct tevent_req *req, return 0; } + +struct dns_cli_request_state { + struct tevent_context *ev; + const char *nameserver; + + uint16_t req_id; + + DATA_BLOB query; + + struct dns_name_packet *reply; +}; + +static void dns_cli_request_udp_done(struct tevent_req *subreq); +static void dns_cli_request_tcp_done(struct tevent_req *subreq); + +struct tevent_req *dns_cli_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *nameserver, + const char *name, + enum dns_qclass qclass, + enum dns_qtype qtype) +{ + struct tevent_req *req, *subreq; + struct dns_cli_request_state *state; + struct dns_name_question question; + struct dns_name_packet out_packet; + enum ndr_err_code ndr_err; + + req = tevent_req_create(mem_ctx, &state, + struct dns_cli_request_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->nameserver = nameserver; + + DBG_DEBUG("Asking %s for %s/%d/%d via UDP\n", nameserver, + name, (int)qclass, (int)qtype); + + generate_random_buffer((uint8_t *)&state->req_id, + sizeof(state->req_id)); + + question = (struct dns_name_question) { + .name = discard_const_p(char, name), + .question_type = qtype, .question_class = qclass + }; + + out_packet = (struct dns_name_packet) { + .id = state->req_id, + .operation = DNS_OPCODE_QUERY | DNS_FLAG_RECURSION_DESIRED, + .qdcount = 1, + .questions = &question + }; + + ndr_err = ndr_push_struct_blob( + &state->query, state, &out_packet, + (ndr_push_flags_fn_t)ndr_push_dns_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + tevent_req_error(req, ndr_map_error2errno(ndr_err)); + return tevent_req_post(req, ev); + } + + subreq = dns_udp_request_send(state, state->ev, state->nameserver, + state->query.data, state->query.length); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, dns_cli_request_udp_done, req); + return req; +} + +static void dns_cli_request_udp_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dns_cli_request_state *state = tevent_req_data( + req, struct dns_cli_request_state); + DATA_BLOB reply; + enum ndr_err_code ndr_err; + int ret; + + ret = dns_udp_request_recv(subreq, state, &reply.data, &reply.length); + TALLOC_FREE(subreq); + if (tevent_req_error(req, ret)) { + return; + } + + state->reply = talloc(state, struct dns_name_packet); + if (tevent_req_nomem(state->reply, req)) { + return; + } + + ndr_err = ndr_pull_struct_blob( + &reply, state->reply, state->reply, + (ndr_pull_flags_fn_t)ndr_pull_dns_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + tevent_req_error(req, ndr_map_error2errno(ndr_err)); + return; + } + TALLOC_FREE(reply.data); + + if (state->reply->id != state->req_id) { + DBG_DEBUG("Got id %"PRIu16", expected %"PRIu16"\n", + state->reply->id, state->req_id); + tevent_req_error(req, ENOMSG); + return; + } + + if ((state->reply->operation & DNS_FLAG_TRUNCATION) == 0) { + DBG_DEBUG("Got op=%x %"PRIu16"/%"PRIu16"/%"PRIu16"/%"PRIu16 + " recs\n", (int)state->reply->operation, + state->reply->qdcount, state->reply->ancount, + state->reply->nscount, state->reply->nscount); + tevent_req_done(req); + return; + } + + DBG_DEBUG("Reply was truncated, retrying TCP\n"); + + TALLOC_FREE(state->reply); + + subreq = dns_tcp_request_send(state, state->ev, state->nameserver, + state->query.data, state->query.length); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, dns_cli_request_tcp_done, req); +} + +static void dns_cli_request_tcp_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct dns_cli_request_state *state = tevent_req_data( + req, struct dns_cli_request_state); + DATA_BLOB reply; + enum ndr_err_code ndr_err; + int ret; + + ret = dns_tcp_request_recv(subreq, state, &reply.data, &reply.length); + TALLOC_FREE(subreq); + if (tevent_req_error(req, ret)) { + return; + } + + state->reply = talloc(state, struct dns_name_packet); + if (tevent_req_nomem(state->reply, req)) { + return; + } + + ndr_err = ndr_pull_struct_blob( + &reply, state->reply, state->reply, + (ndr_pull_flags_fn_t)ndr_pull_dns_name_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + tevent_req_error(req, ndr_map_error2errno(ndr_err)); + return; + } + TALLOC_FREE(reply.data); + + if (state->reply->id != state->req_id) { + DBG_DEBUG("Got id %"PRIu16", expected %"PRIu16"\n", + state->reply->id, state->req_id); + tevent_req_error(req, ENOMSG); + return; + } + + DBG_DEBUG("Got op=%x %"PRIu16"/%"PRIu16"/%"PRIu16"/%"PRIu16 + " recs\n", (int)state->reply->operation, + state->reply->qdcount, state->reply->ancount, + state->reply->nscount, state->reply->nscount); + + tevent_req_done(req); +} + +int dns_cli_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct dns_name_packet **reply) +{ + struct dns_cli_request_state *state = tevent_req_data( + req, struct dns_cli_request_state); + int err; + + if (tevent_req_is_unix_error(req, &err)) { + return err; + } + *reply = talloc_move(mem_ctx, &state->reply); + return 0; +} diff --git a/libcli/dns/libdns.h b/libcli/dns/libdns.h index 1fa73d3fb22..1b7c404f6b7 100644 --- a/libcli/dns/libdns.h +++ b/libcli/dns/libdns.h @@ -22,6 +22,10 @@ #ifndef __LIBDNS_H__ #define __LIBDNS_H__ +#include "lib/util/data_blob.h" +#include "lib/util/time.h" +#include "librpc/gen_ndr/dns.h" + /** Send an dns request to a dns server using UDP * *@param mem_ctx talloc memory context to use @@ -60,4 +64,18 @@ int dns_tcp_request_recv(struct tevent_req *req, uint8_t **reply, size_t *reply_len); +/* + * DNS request with fallback to TCP on truncation + */ + +struct tevent_req *dns_cli_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *nameserver, + const char *name, + enum dns_qclass qclass, + enum dns_qtype qtype); +int dns_cli_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct dns_name_packet **reply); + + #endif /*__LIBDNS_H__*/ diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build index 018df6b387e..2dcd8c17d9d 100644 --- a/libcli/dns/wscript_build +++ b/libcli/dns/wscript_build @@ -2,4 +2,4 @@ bld.SAMBA_SUBSYSTEM('clidns', source='dns.c', - public_deps='LIBTSOCKET tevent-util') + public_deps='LIBTSOCKET tevent-util NDR_DNS') -- cgit v1.2.1