diff options
author | Volker Lendecke <vl@samba.org> | 2017-12-29 11:01:29 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2018-01-04 00:37:21 +0100 |
commit | 0bb92d7f377caffb2425cc7757b370bcf671e598 (patch) | |
tree | 948f0d8831c9ee9fe960c912065845a09957b67c /libcli/dns/dns.c | |
parent | 623883083b5f9b5f07466fee99080c0c7f588551 (diff) | |
download | samba-0bb92d7f377caffb2425cc7757b370bcf671e598.tar.gz |
libdns: Add dns_cli_request
First UDP, then TCP if truncation happened
Signed-off-by: Volker Lendecke <vl@samba.org>
Diffstat (limited to 'libcli/dns/dns.c')
-rw-r--r-- | libcli/dns/dns.c | 191 |
1 files changed, 190 insertions, 1 deletions
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; +} |