summaryrefslogtreecommitdiffstats
path: root/libcli/dns
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libcli/dns/dns.c591
-rw-r--r--libcli/dns/dns.h64
-rw-r--r--libcli/dns/dns_lookup.c374
-rw-r--r--libcli/dns/dns_lookup.h48
-rw-r--r--libcli/dns/dns_lookuptest.c55
-rw-r--r--libcli/dns/libdns.h43
-rw-r--r--libcli/dns/resolvconf.c123
-rw-r--r--libcli/dns/resolvconf.h37
-rw-r--r--libcli/dns/resolvconftest.c82
-rw-r--r--libcli/dns/wscript_build21
10 files changed, 1438 insertions, 0 deletions
diff --git a/libcli/dns/dns.c b/libcli/dns/dns.c
new file mode 100644
index 0000000..943b4d5
--- /dev/null
+++ b/libcli/dns/dns.c
@@ -0,0 +1,591 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Small async DNS library for Samba with socketwrapper support
+
+ Copyright (C) 2010 Kai Blin <kai@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#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/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+
+struct dns_udp_request_state {
+ struct tevent_context *ev;
+ struct tdgram_context *dgram;
+ size_t query_len;
+ uint8_t *reply;
+ size_t reply_len;
+};
+
+#define DNS_REQUEST_TIMEOUT 10
+
+/* Declare callback functions used below. */
+static void dns_udp_request_get_reply(struct tevent_req *subreq);
+static void dns_udp_request_done(struct tevent_req *subreq);
+
+static struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ const uint8_t *query,
+ size_t query_len)
+{
+ struct tevent_req *req, *subreq;
+ struct dns_udp_request_state *state;
+ struct tsocket_address *local_addr, *server_addr;
+ struct tdgram_context *dgram;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_udp_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ /* Use connected UDP sockets */
+ ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+ &local_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_hostport_strings(
+ state, "ip", server_addr_string, DNS_SERVICE_PORT, &server_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ state->dgram = dgram;
+ state->query_len = query_len;
+
+ dump_data(10, query, query_len);
+
+ subreq = tdgram_sendto_send(state, ev, dgram, query, query_len, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (!tevent_req_set_endtime(req, ev,
+ timeval_current_ofs(DNS_REQUEST_TIMEOUT, 0))) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq, dns_udp_request_get_reply, req);
+ return req;
+}
+
+static void dns_udp_request_get_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_sendto_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (len != state->query_len) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, dns_udp_request_done, req);
+}
+
+static void dns_udp_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_recvfrom_recv(subreq, &err, state, &state->reply, NULL);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ state->reply_len = len;
+ dump_data(10, state->reply, state->reply_len);
+ tevent_req_done(req);
+}
+
+static int dns_udp_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len)
+{
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *reply = talloc_move(mem_ctx, &state->reply);
+ *reply_len = state->reply_len;
+ tevent_req_received(req);
+
+ return 0;
+}
+
+struct dns_tcp_request_state {
+ struct tevent_context *ev;
+ struct tstream_context *stream;
+ const uint8_t *query;
+ size_t query_len;
+
+ uint8_t dns_msglen_hdr[2];
+ struct iovec iov[2];
+
+ size_t nread;
+ uint8_t *reply;
+};
+
+static void dns_tcp_request_connected(struct tevent_req *subreq);
+static void dns_tcp_request_sent(struct tevent_req *subreq);
+static int dns_tcp_request_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *_count);
+static void dns_tcp_request_received(struct tevent_req *subreq);
+
+static struct tevent_req *dns_tcp_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ const uint8_t *query,
+ size_t query_len)
+{
+ struct tevent_req *req, *subreq;
+ struct dns_tcp_request_state *state;
+ struct tsocket_address *local, *remote;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct dns_tcp_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->query = query;
+ state->query_len = query_len;
+
+ if (query_len > UINT16_MAX) {
+ tevent_req_error(req, EMSGSIZE);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0, &local);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_hostport_strings(
+ state, "ip", server_addr_string, DNS_SERVICE_PORT, &remote);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = tstream_inet_tcp_connect_send(state, state->ev,
+ local, remote);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, dns_tcp_request_connected, req);
+
+ return req;
+}
+
+static void dns_tcp_request_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dns_tcp_request_state *state = tevent_req_data(
+ req, struct dns_tcp_request_state);
+ int ret, err;
+
+ ret = tstream_inet_tcp_connect_recv(subreq, &err, state,
+ &state->stream, NULL);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ RSSVAL(state->dns_msglen_hdr, 0, state->query_len);
+ state->iov[0] = (struct iovec) {
+ .iov_base = state->dns_msglen_hdr,
+ .iov_len = sizeof(state->dns_msglen_hdr)
+ };
+ state->iov[1] = (struct iovec) {
+ .iov_base = discard_const_p(void, state->query),
+ .iov_len = state->query_len
+ };
+
+ subreq = tstream_writev_send(state, state->ev, state->stream,
+ state->iov, ARRAY_SIZE(state->iov));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dns_tcp_request_sent, req);
+}
+
+static void dns_tcp_request_sent(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dns_tcp_request_state *state = tevent_req_data(
+ req, struct dns_tcp_request_state);
+ int ret, err;
+
+ ret = tstream_writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ subreq = tstream_readv_pdu_send(state, state->ev, state->stream,
+ dns_tcp_request_next_vector, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, dns_tcp_request_received, req);
+}
+
+static int dns_tcp_request_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *_count)
+{
+ struct dns_tcp_request_state *state = talloc_get_type_abort(
+ private_data, struct dns_tcp_request_state);
+ struct iovec *vector;
+ uint16_t msglen;
+
+ if (state->nread == 0) {
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (vector == NULL) {
+ return -1;
+ }
+ vector[0] = (struct iovec) {
+ .iov_base = state->dns_msglen_hdr,
+ .iov_len = sizeof(state->dns_msglen_hdr)
+ };
+ state->nread = sizeof(state->dns_msglen_hdr);
+
+ *_vector = vector;
+ *_count = 1;
+ return 0;
+ }
+
+ if (state->nread == sizeof(state->dns_msglen_hdr)) {
+ msglen = RSVAL(state->dns_msglen_hdr, 0);
+
+ state->reply = talloc_array(state, uint8_t, msglen);
+ if (state->reply == NULL) {
+ return -1;
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (vector == NULL) {
+ return -1;
+ }
+ vector[0] = (struct iovec) {
+ .iov_base = state->reply,
+ .iov_len = msglen
+ };
+ state->nread += msglen;
+
+ *_vector = vector;
+ *_count = 1;
+ return 0;
+ }
+
+ *_vector = NULL;
+ *_count = 0;
+ return 0;
+}
+
+static void dns_tcp_request_received(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret, err;
+
+ ret = tstream_readv_pdu_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int dns_tcp_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len)
+{
+ struct dns_tcp_request_state *state = tevent_req_data(
+ req, struct dns_tcp_request_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *reply_len = talloc_array_length(state->reply);
+ *reply = talloc_move(mem_ctx, &state->reply);
+ tevent_req_received(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;
+ uint16_t reply_id, operation;
+ int ret;
+
+ ret = dns_udp_request_recv(subreq, state, &reply.data, &reply.length);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ if (reply.length < 4) {
+ DBG_DEBUG("Short DNS packet: length=%zu\n", reply.length);
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ reply_id = PULL_BE_U16(reply.data, 0);
+ if (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;
+ }
+
+ operation = PULL_BE_U16(reply.data, 2);
+ if ((operation & DNS_FLAG_TRUNCATION) != 0) {
+ DBG_DEBUG("Reply was truncated, retrying TCP\n");
+ 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);
+ 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);
+
+ tevent_req_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/dns.h b/libcli/dns/dns.h
new file mode 100644
index 0000000..34a6cf5
--- /dev/null
+++ b/libcli/dns/dns.h
@@ -0,0 +1,64 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Gerald Carter 2006.
+ * Copyright (C) Andrew Bartlett 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* DNS query section in replies */
+
+#ifndef __LIBCLI_DNS_DNS_H__
+#define __LIBCLI_DNS_DNS_H__
+
+#include "replace.h"
+#include "system/network.h"
+
+struct dns_query {
+ const char *hostname;
+ uint16_t type;
+ uint16_t in_class;
+};
+
+/* DNS RR record in reply */
+
+struct dns_rr {
+ const char *hostname;
+ uint16_t type;
+ uint16_t in_class;
+ uint32_t ttl;
+ uint16_t rdatalen;
+ uint8_t *rdata;
+};
+
+/* SRV records */
+
+struct dns_rr_srv {
+ const char *hostname;
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ size_t num_ips;
+ struct sockaddr_storage *ss_s; /* support multi-homed hosts */
+};
+
+/* NS records */
+
+struct dns_rr_ns {
+ const char *hostname;
+ struct sockaddr_storage ss;
+};
+
+#endif
diff --git a/libcli/dns/dns_lookup.c b/libcli/dns/dns_lookup.c
new file mode 100644
index 0000000..cb0ede8
--- /dev/null
+++ b/libcli/dns/dns_lookup.c
@@ -0,0 +1,374 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "libcli/dns/dns_lookup.h"
+#include "libcli/dns/resolvconf.h"
+#include "libcli/dns/libdns.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/samba_util.h"
+#include "lib/util/debug.h"
+
+struct dns_lookup_state {
+ struct tevent_context *ev;
+ const char *name;
+ enum dns_qclass qclass;
+ enum dns_qtype qtype;
+
+ char **nameservers;
+ size_t num_nameservers;
+ size_t num_sent;
+
+ struct tevent_req **dns_subreqs;
+ struct tevent_req *wait_subreq;
+
+ struct dns_name_packet *reply;
+};
+
+static int dns_lookup_send_next(struct tevent_req *req);
+
+static void dns_lookup_done(struct tevent_req *subreq);
+static void dns_lookup_waited(struct tevent_req *subreq);
+
+struct tevent_req *dns_lookup_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ FILE *resolv_conf_fp,
+ const char *name,
+ enum dns_qclass qclass,
+ enum dns_qtype qtype)
+{
+ struct tevent_req *req;
+ struct dns_lookup_state *state;
+ FILE *fp = resolv_conf_fp;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_lookup_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->name = name;
+ state->qclass = qclass;
+ state->qtype = qtype;
+
+ if (resolv_conf_fp == NULL) {
+ const char *resolvconf = "/etc/resolv.conf";
+
+#ifdef ENABLE_SELFTEST
+ {
+ const char *envvar = getenv("RESOLV_CONF");
+ if (envvar != NULL) {
+ resolvconf = envvar;
+ }
+ }
+#endif
+
+ fp = fopen(resolvconf, "r");
+ if (fp == NULL) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ ret = parse_resolvconf_fp(
+ fp,
+ state,
+ &state->nameservers,
+ &state->num_nameservers);
+
+ if (resolv_conf_fp == NULL) {
+ fclose(fp);
+ }
+
+ if (ret != 0) {
+ tevent_req_error(req, ret);
+ return tevent_req_post(req, ev);
+ }
+
+ if (state->num_nameservers == 0) {
+ /*
+ * glibc's getaddrinfo returns EAI_AGAIN when no
+ * nameservers are configured. EAGAIN seems closest.
+ */
+ tevent_req_error(req, EAGAIN);
+ return tevent_req_post(req, ev);
+ }
+
+ state->dns_subreqs = talloc_zero_array(
+ state,
+ struct tevent_req *,
+ state->num_nameservers);
+
+ if (tevent_req_nomem(state->dns_subreqs, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ret = dns_lookup_send_next(req);
+ if (tevent_req_error(req, ret)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static int dns_lookup_send_next(struct tevent_req *req)
+{
+ struct dns_lookup_state *state = tevent_req_data(
+ req, struct dns_lookup_state);
+
+ DBG_DEBUG("Sending DNS request #%zu to %s\n",
+ state->num_sent,
+ state->nameservers[state->num_sent]);
+
+ state->dns_subreqs[state->num_sent] = dns_cli_request_send(
+ state->dns_subreqs,
+ state->ev,
+ state->nameservers[state->num_sent],
+ state->name,
+ state->qclass,
+ state->qtype);
+
+ if (state->dns_subreqs[state->num_sent] == NULL) {
+ return ENOMEM;
+ }
+ tevent_req_set_callback(state->dns_subreqs[state->num_sent],
+ dns_lookup_done,
+ req);
+ state->num_sent += 1;
+
+ if (state->num_sent == state->num_nameservers) {
+ /*
+ * No more nameservers left
+ */
+ DBG_DEBUG("cancelling wait_subreq\n");
+ TALLOC_FREE(state->wait_subreq);
+ return 0;
+ }
+
+ if (state->wait_subreq != NULL) {
+ /*
+ * This can happen if we fire the next request upon
+ * dns_cli_request returning a network-level error
+ */
+ return 0;
+ }
+
+ state->wait_subreq = tevent_wakeup_send(
+ state,
+ state->ev,
+ tevent_timeval_current_ofs(1, 0));
+ if (state->wait_subreq == NULL) {
+ return ENOMEM;
+ }
+ tevent_req_set_callback(state->wait_subreq, dns_lookup_waited, req);
+
+ return 0;
+}
+
+static void dns_lookup_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dns_lookup_state *state = tevent_req_data(
+ req, struct dns_lookup_state);
+ int dns_cli_request_ret;
+ size_t i;
+
+ dns_cli_request_ret = dns_cli_request_recv(
+ subreq,
+ state,
+ &state->reply);
+
+ for (i = 0; i < state->num_nameservers; i++) {
+ if (state->dns_subreqs[i] == subreq) {
+ break;
+ }
+ }
+
+ TALLOC_FREE(subreq);
+
+ if (i == state->num_nameservers) {
+ /* should never happen */
+ DBG_WARNING("Failed to find subreq");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+ state->dns_subreqs[i] = NULL;
+
+ if (dns_cli_request_ret == 0) {
+ /*
+ * Success, cancel everything else
+ */
+ TALLOC_FREE(state->dns_subreqs);
+ TALLOC_FREE(state->wait_subreq);
+ tevent_req_done(req);
+ return;
+ }
+
+ DBG_DEBUG("dns_cli_request[%zu] returned %s\n", i,
+ strerror(dns_cli_request_ret));
+
+ if (state->num_sent < state->num_nameservers) {
+ /*
+ * We have a nameserver left to try
+ */
+ int ret;
+
+ ret = dns_lookup_send_next(req);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ }
+
+ DBG_DEBUG("looking for outstanding requests\n");
+
+ for (i = 0; i<state->num_nameservers; i++) {
+ if (state->dns_subreqs[i] != NULL) {
+ break;
+ }
+ }
+
+ DBG_DEBUG("i=%zu, num_nameservers=%zu\n",
+ i, state->num_nameservers);
+
+ if (i == state->num_nameservers) {
+ /*
+ * Report the lower-level error if we have nothing
+ * outstanding anymore
+ */
+ tevent_req_error(req, dns_cli_request_ret);
+ return;
+ }
+
+ /*
+ * Do nothing: We have other nameservers that might come back
+ * with something good.
+ */
+}
+
+static void dns_lookup_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dns_lookup_state *state = tevent_req_data(
+ req, struct dns_lookup_state);
+ int ret;
+ bool ok;
+
+ DBG_DEBUG("waited\n");
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ state->wait_subreq = NULL;
+
+ ret = dns_lookup_send_next(req);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ /*
+ * dns_lookup_send_next() has already triggered the next wakeup
+ */
+}
+
+int dns_lookup_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct dns_name_packet **reply)
+{
+ struct dns_lookup_state *state = tevent_req_data(
+ req, struct dns_lookup_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+
+ *reply = talloc_move(mem_ctx, &state->reply);
+
+ tevent_req_received(req);
+ return 0;
+}
+
+int dns_lookup(FILE *resolv_conf_fp,
+ const char *name,
+ enum dns_qclass qclass,
+ enum dns_qtype qtype,
+ TALLOC_CTX *mem_ctx,
+ struct dns_name_packet **reply)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ int ret = ENOMEM;
+
+ ev = samba_tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = dns_lookup_send(ev, ev, resolv_conf_fp, name, qclass, qtype);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_unix(req, ev, &ret)) {
+ goto fail;
+ }
+ ret = dns_lookup_recv(req, mem_ctx, reply);
+fail:
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+bool dns_res_rec_get_sockaddr(const struct dns_res_rec *rec,
+ struct sockaddr_storage *addr)
+{
+ sa_family_t family;
+ const char *src;
+ void *dst;
+ int ret;
+
+ switch (rec->rr_type) {
+ case DNS_QTYPE_A:
+ family = AF_INET;
+ src = rec->rdata.ipv4_record;
+ dst = &(((struct sockaddr_in *)addr)->sin_addr);
+ break;
+#ifdef HAVE_IPV6
+ case DNS_QTYPE_AAAA:
+ family = AF_INET6;
+ src = rec->rdata.ipv6_record;
+ dst = &(((struct sockaddr_in6 *)addr)->sin6_addr);
+ break;
+#endif
+ default:
+ /* We only care about IP addresses */
+ return false;
+ }
+
+ *addr = (struct sockaddr_storage) { .ss_family = family };
+
+ ret = inet_pton(family, src, dst);
+ if (ret != 1) {
+ DBG_DEBUG("inet_pton(%s) failed\n", src);
+ return false;
+ }
+
+ return true;
+}
diff --git a/libcli/dns/dns_lookup.h b/libcli/dns/dns_lookup.h
new file mode 100644
index 0000000..0c05e04
--- /dev/null
+++ b/libcli/dns/dns_lookup.h
@@ -0,0 +1,48 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBCLI_DNS_DNS_LOOKUP_H__
+#define __LIBCLI_DNS_DNS_LOOKUP_H__
+
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "librpc/gen_ndr/dns.h"
+
+struct tevent_req *dns_lookup_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ FILE *resolv_conf_fp,
+ const char *name,
+ enum dns_qclass qclass,
+ enum dns_qtype qtype);
+int dns_lookup_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct dns_name_packet **reply);
+int dns_lookup(FILE *resolv_conf_fp,
+ const char *name,
+ enum dns_qclass qclass,
+ enum dns_qtype qtype,
+ TALLOC_CTX *mem_ctx,
+ struct dns_name_packet **reply);
+
+bool dns_res_rec_get_sockaddr(const struct dns_res_rec *rec,
+ struct sockaddr_storage *addr);
+
+#endif
diff --git a/libcli/dns/dns_lookuptest.c b/libcli/dns/dns_lookuptest.c
new file mode 100644
index 0000000..c8e0343
--- /dev/null
+++ b/libcli/dns/dns_lookuptest.c
@@ -0,0 +1,55 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <talloc.h>
+#include <errno.h>
+#include "libcli/dns/dns_lookup.h"
+#include "lib/util/debug.h"
+
+static int dns_lookuptest1(void)
+{
+ struct dns_name_packet *reply = NULL;
+ int ret;
+
+ ret = dns_lookup(NULL, "www.samba.org", DNS_QCLASS_IN, DNS_QTYPE_A,
+ NULL, &reply);
+ if (ret != 0) {
+ fprintf(stderr, "dns_lookup failed: %s\n", strerror(ret));
+ return ret;
+ }
+
+ TALLOC_FREE(reply);
+ return 0;
+}
+
+int main(int argc, const char *argv[]) {
+ int ret;
+
+ setup_logging(argv[0], DEBUG_DEFAULT_STDERR);
+ debug_parse_levels("10");
+
+ ret = dns_lookuptest1();
+ if (ret != 0) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libcli/dns/libdns.h b/libcli/dns/libdns.h
new file mode 100644
index 0000000..15ca257
--- /dev/null
+++ b/libcli/dns/libdns.h
@@ -0,0 +1,43 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Small async DNS library for Samba with socketwrapper support
+
+ Copyright (C) 2012 Kai Blin <kai@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBDNS_H__
+#define __LIBDNS_H__
+
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "librpc/gen_ndr/dns.h"
+
+/*
+ * 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/resolvconf.c b/libcli/dns/resolvconf.c
new file mode 100644
index 0000000..5cf8b4e
--- /dev/null
+++ b/libcli/dns/resolvconf.c
@@ -0,0 +1,123 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <stdio.h>
+#include <errno.h>
+#include "libcli/dns/resolvconf.h"
+#include "lib/util/memory.h"
+
+int parse_resolvconf_fp(
+ FILE *fp,
+ TALLOC_CTX *mem_ctx,
+ char ***pnameservers,
+ size_t *pnum_nameservers)
+{
+ char *line = NULL;
+ size_t len = 0;
+ char **nameservers = NULL;
+ size_t num_nameservers = 0;
+ int ret = 0;
+
+ while (true) {
+ char *saveptr = NULL, *option = NULL, *ns = NULL;
+ char **tmp = NULL;
+ ssize_t n = 0;
+
+ n = getline(&line, &len, fp);
+ if (n < 0) {
+ if (!feof(fp)) {
+ /* real error */
+ ret = errno;
+ }
+ break;
+ }
+ if ((n > 0) && (line[n-1] == '\n')) {
+ line[n-1] = '\0';
+ }
+
+ if ((line[0] == '#') || (line[0] == ';')) {
+ continue;
+ }
+
+ option = strtok_r(line, " \t", &saveptr);
+ if (option == NULL) {
+ continue;
+ }
+
+ if (strcmp(option, "nameserver") != 0) {
+ continue;
+ }
+
+ ns = strtok_r(NULL, " \t", &saveptr);
+ if (ns == NULL) {
+ continue;
+ }
+
+ tmp = talloc_realloc(
+ mem_ctx,
+ nameservers,
+ char *,
+ num_nameservers+1);
+ if (tmp == NULL) {
+ ret = ENOMEM;
+ break;
+ }
+ nameservers = tmp;
+
+ nameservers[num_nameservers] = talloc_strdup(nameservers, ns);
+ if (nameservers[num_nameservers] == NULL) {
+ ret = ENOMEM;
+ break;
+ }
+ num_nameservers += 1;
+ }
+
+ SAFE_FREE(line);
+
+ if (ret == 0) {
+ *pnameservers = nameservers;
+ *pnum_nameservers = num_nameservers;
+ } else {
+ TALLOC_FREE(nameservers);
+ }
+
+ return ret;
+}
+
+int parse_resolvconf(
+ const char *resolvconf,
+ TALLOC_CTX *mem_ctx,
+ char ***pnameservers,
+ size_t *pnum_nameservers)
+{
+ FILE *fp;
+ int ret;
+
+ fp = fopen(resolvconf ? resolvconf : "/etc/resolv.conf", "r");
+ if (fp == NULL) {
+ return errno;
+ }
+
+ ret = parse_resolvconf_fp(fp, mem_ctx, pnameservers, pnum_nameservers);
+
+ fclose(fp);
+
+ return ret;
+}
diff --git a/libcli/dns/resolvconf.h b/libcli/dns/resolvconf.h
new file mode 100644
index 0000000..3ea258c
--- /dev/null
+++ b/libcli/dns/resolvconf.h
@@ -0,0 +1,37 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBCLI_DNS_RESOLVCONF_H__
+#define __LIBCLI_DNS_RESOLVCONF_H__
+
+#include <talloc.h>
+#include <stdio.h>
+
+int parse_resolvconf_fp(
+ FILE *fp,
+ TALLOC_CTX *mem_ctx,
+ char ***pnameservers,
+ size_t *pnum_nameservers);
+int parse_resolvconf(
+ const char *resolvconf,
+ TALLOC_CTX *mem_ctx,
+ char ***pnameservers,
+ size_t *pnum_nameservers);
+
+#endif
diff --git a/libcli/dns/resolvconftest.c b/libcli/dns/resolvconftest.c
new file mode 100644
index 0000000..6395264
--- /dev/null
+++ b/libcli/dns/resolvconftest.c
@@ -0,0 +1,82 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Internal DNS query structures
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <talloc.h>
+#include <errno.h>
+#include "libcli/dns/resolvconf.h"
+#include "lib/util/memory.h"
+
+static int resolvconftest1(void)
+{
+ const char *content =
+ "#foo\nbar\nnameserver 1.2.3.4\nnameserver 2.3.4.5";
+ char *file;
+ FILE *fp;
+ int ret;
+ char **nameservers;
+ size_t num_nameservers;
+
+ file = strdup(content);
+ if (file == NULL) {
+ perror("strdup failed");
+ return ENOMEM;
+ }
+ fp = fmemopen(file, strlen(file), "r");
+ if (fp == NULL) {
+ perror("fmemopen failed");
+ return errno;
+ }
+
+ ret = parse_resolvconf_fp(fp, NULL, &nameservers, &num_nameservers);
+ if (ret != 0) {
+ fprintf(stderr, "parse_resolvconf_fp failed: %s\n",
+ strerror(ret));
+ return ret;
+ }
+
+ if (num_nameservers != 2) {
+ fprintf(stderr, "expected 2 nameservers, got %zu\n",
+ num_nameservers);
+ return EIO;
+ }
+ if ((strcmp(nameservers[0], "1.2.3.4") != 0) ||
+ (strcmp(nameservers[1], "2.3.4.5") != 0)) {
+ fprintf(stderr, "got wrong nameservers\n");
+ return EIO;
+ }
+
+ TALLOC_FREE(nameservers);
+ fclose(fp);
+ SAFE_FREE(file);
+
+ return 0;
+}
+
+int main(void) {
+ int ret;
+
+ ret = resolvconftest1();
+ if (ret != 0) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build
new file mode 100644
index 0000000..2d90aa7
--- /dev/null
+++ b/libcli/dns/wscript_build
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('clidns',
+ source='dns.c resolvconf.c',
+ public_deps='LIBTSOCKET tevent-util ndr-standard',
+ private_library=True)
+
+bld.SAMBA_BINARY('resolvconftest',
+ source='resolvconftest.c',
+ deps='clidns',
+ enabled=bld.CONFIG_SET('HAVE_FMEMOPEN'),
+ for_selftest=True)
+
+bld.SAMBA_SUBSYSTEM('dns_lookup',
+ source='dns_lookup.c',
+ public_deps='clidns')
+
+bld.SAMBA_BINARY('dns_lookuptest',
+ source='dns_lookuptest.c',
+ deps='dns_lookup',
+ for_selftest=True)