[PATCHES]GSoC 2017 Project libcli/dns patches

Dimitris Gravanis dimgrav at gmail.com
Sun Aug 27 11:59:14 UTC 2017


Hi,

I attach all the changes made and integrated to dimgrav/samba 
<https://github.com/dimgrav/samba> for reviewing. All organized in a 
single txt file, which contains the required commit patches.

Essentialy, there is addition for client-side TCP async request 
send/receive and GSS-TSIG signing, along some test suites for every 
feature added.

There are READMEs that document everything and the code is commented 
enough I think, to be easily readable and clean.

Waf configures and builds the fork code with no errors (yay!).

Let me know if you'd prefer single patches per commit instead.

Cheers,
Dimitris

-- 
Δ. Γραβάνης | D. Gravanis

-------------- next part --------------
From 6fe1afd181635b46c97619530915353cec1135ae Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:11:01 +0300
Subject: [PATCH 01/23] libcli/dns: added README

---
 libcli/dns/README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 libcli/dns/README.md

diff --git a/libcli/dns/README.md b/libcli/dns/README.md
new file mode 100644
index 0000000..f5a1c53
--- /dev/null
+++ b/libcli/dns/README.md
@@ -0,0 +1,76 @@
+# Client-side DNS call handling with GSS-TSIG
+### Unix SMB/CIFS implementation
+### Dimitrios Gravanis (C) 2017
+### Based on the existing work by Samba Team
+
+--------------------------------------------------------
+About
+--------------------------------------------------------
+
+For the Samba AD DC, libcli/dns is a library that allows the handling of DNS 
+calls (send/receive requests) and generates GSS-TSIG type encryption signature 
+for signed packets, to accomodate encrypted client-server communication.
+
+It consists of its respective function and structure libraries, that provide 
+definitions for client-side functionality.
+
+Test suites are also available, that inspect individual features of cli_dns.c
+
+For more information on the project goals, read the GSoC proposal [here](https://summerofcode.withgoogle.com/projects/#6642229069217792).
+
+The project timeline and development journal is documented in its dedicated [blogspot](https://dimgrav.blogspot.gr/).
+
+--------------------------------------------------------
+Content listing and descriptions
+--------------------------------------------------------
+
+1. cli-fn
+
+	* client_crypto.c: GSS-TSIG client-side handling for signed packets
+	* dns_tcp.c: TCP client-side DNS call handling
+	* dns_udp.c: Small async DNS library for Samba with socketwrapper support (pre-existing dns.c)
+
+2. cmocka-tests
+
+	* cli_crypto_test.c: Tests GSS-TSIG client-side handling for signed $
+	* dns_tcp_test.c: Tests TCP client-side DNS call handling
+	* dns_udp_test.c: Tests UDP client-side DNS call handling
+	* cli_tests: Complete test suite for libcli/dns/cli_dns.c
+
+3. cli_dns.c: DNS UDP/TCP call handler with socketwrapper support and TSIG generation (replaces dns.c)
+
+4. dns.h: Internal DNS query structures
+
+5. libtcp.h: TCP client-side DNS structures
+
+6. libtsig.h: GSS-TSIG client-side DNS structures and utilites
+
+7. libudp.h: Small async DNS library for Samba with socketwrapper support (pre-existing libdns.h)
+
+8. libwrap.h: DNS UDP/TCP send/recv wrap library with TSIG generation
+
+9. wrap_cli.c: DNS UDP/TCP send/recv wrapping with TSIG generation
+
+--------------------------------------------------------
+DNS Client (with wrapper support)
+--------------------------------------------------------
+
+Handles TCP and UDP requests.
+
+The client may use either TCP or UDP protocols to send a DNS name request to
+the server, then handle the reception of the appropriate server response.
+
+Features:
+
+* UDP request send/receive
+* TCP request send/receive
+* GSS-TSIG generation
+* DNS name packet parsing and signing
+
+The library consists of cli_dns.c, that includes functions, and dns.h, libtcp.h, 
+libtsig.h, libudp.h, that provide declarations, definitions and structures.
+
+### Wrapping
+wrap_cli.c provides multiple wrapping of the above functionality, to hide buffer
+creation, DNS packet parsing and signature generation. Definitions of the wrapped
+functions are provided in libwrap.h.
-- 
2.7.4


From be0bc659c08a3e6706b616cee92817889781cec5 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:12:20 +0300
Subject: [PATCH 02/23] libcli/dns/cli_dns: library for TCP/UDP async requests
 using GSS-TSIG

---
 libcli/dns/cli_dns.c | 541 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 541 insertions(+)
 create mode 100644 libcli/dns/cli_dns.c

diff --git a/libcli/dns/cli_dns.c b/libcli/dns/cli_dns.c
new file mode 100644
index 0000000..ab41e09
--- /dev/null
+++ b/libcli/dns/cli_dns.c
@@ -0,0 +1,541 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   DNS UDP/TCP call handler with socketwrapper support and TSIG generation
+
+   Copyright (C) 2017 Dimitrios Gravanis <dimgrav at gmail.com>
+
+   Based on:
+
+   DNS server startup
+   DNS structures
+   Small async DNS library for Samba with socketwrapper support
+
+   Copyright (C) 2010 Kai Blin  <kai at 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/>.
+*/
+
+/* TSIG generation */
+#include "includes.h"
+#include "lib/crypto/hmacmd5.h"
+#include "libcli/util/ntstatus.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "source4/dns_server/dns_server.h"
+#include "libcli/dns/libtsig.h"
+
+/* DNS call send/recv() */
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "source4/smbd/service_task.h"
+#include "source4/smbd/service_stream.h"
+#include "source4/lib/stream/packet.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "libcli/dns/libtcp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+
+#define DNS_REQUEST_TIMEOUT 2
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
+
+/*** UDP Requests ***/
+
+/* UDP state struct */
+struct dns_udp_request_state {
+	struct tevent_context *ev;
+	struct tdgram_context *dgram;
+	size_t query_len;
+	uint8_t *reply;
+	size_t reply_len;
+};
+
+/* UDP callbacks */
+void dns_udp_request_get_reply(struct tevent_req *subreq);
+void dns_udp_request_done(struct tevent_req *subreq);
+
+/* udp request to send */
+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_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))) {
+		tevent_req_oom(req);
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_set_callback(subreq, dns_udp_request_get_reply, req);
+	return req;
+}
+
+/* wait for server reply */
+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);
+}
+
+/* callback status */
+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);
+}
+
+/* receiver */
+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;
+}
+
+/*** TCP Requests ***/
+
+/* TCP callbacks */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq);
+void dns_tcp_req_done(struct tevent_req *subreq);
+
+/* tcp request to send */
+struct tevent_req *dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					const char *server_addr_string,
+					struct iovec *vector,
+					size_t count)
+{
+	struct tevent_req *req, *subreq, *socreq;
+	struct dns_tcp_request_state *state;
+	struct tsocket_address *local_address, *remote_address;
+	struct tstream_context *stream;
+	int req_ret, soc_ret, err;
+
+	req = tevent_req_create(mem_ctx, &state, struct dns_tcp_request_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	state->ev = ev;
+
+	/* check for connected sockets and use if any */
+	req_ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+						&local_address);
+	if (req_ret != 0) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	req_ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+						DNS_SERVICE_PORT, &remote_address);
+	if (req_ret != 0) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	/* must be reviewed! */
+	soc_ret = tstream_inet_tcp_connect_recv(socreq, err, mem_ctx, stream, NULL);
+	TALLOC_FREE(socreq);
+	if (soc_ret == -1 && err != 0) {
+		tevent_req_error(socreq, err);
+		return tevent_req_post(req, ev);
+	}
+
+	socreq = tstream_inet_tcp_connect_send(mem_ctx, ev, local_address, remote_address);
+	if (tevent_req_nomem(socreq, req)) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(socreq, dns_tcp_req_send, req);
+
+	state->tstream = stream;
+	state->v_count = count;
+
+	subreq = tstream_writev_send(mem_ctx, ev, stream, vector, count);
+	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))) {
+		tevent_req_oom(req);
+		return tevent_req_post(req, ev);
+	}
+
+	/* associate callback */
+	tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, req);
+	
+	return req;
+}
+
+/* get buffer and wait to receive server response */
+void dns_tcp_req_recv_reply(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);
+	ssize_t stream_len;
+	int err = 0;
+	NTSTATUS status;
+
+	stream_len = tstream_writev_recv(subreq, &err);
+	TALLOC_FREE(subreq);
+
+	if (stream_len == -1 && err != 0) {
+		tevent_req_error(req, err);
+		return;
+	}
+
+	if (stream_len != state->v_count) {
+		tevent_req_error(req, EIO);
+		return;
+	}
+
+	/* response loop */
+	struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+			struct dns_tcp_connection);
+	struct tsocket_address *local_address, *server_address;
+	struct dns_client *dns = dns_conn->dns_socket->dns; // uses server iface
+	struct dns_tcp_call *call;
+
+	call = talloc(dns_conn, struct dns_tcp_call);
+	if (call == NULL) {
+		DEBUG(1, ("dns_tcp_req_recv_reply: NULL call\n"));
+		return;
+	}
+	call->dns_conn = dns_conn;
+
+	status = tstream_read_pdu_blob_recv(subreq, call, &call->in);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("tstream_read_pdu_blob_recv: error %s\n", nt_errstr(status)));
+		return;
+	}
+	
+	if (subreq == NULL) {
+		DEBUG(1, ("dns_tcp_req_recv_reply: NULL subreq\n"));
+		return;
+	}
+	tevent_req_set_callback(subreq, dns_tcp_req_done, call);
+	TALLOC_FREE(subreq);
+	
+	subreq = tstream_read_pdu_blob_send(dns_conn,
+					    dns_conn->conn->event.ctx,
+					    dns_conn->tstream,
+					    2, 
+					    packet_full_request_u16,
+					    dns_conn);
+	/* loop callback */
+	tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, dns_conn);
+}
+
+/* callback status */
+void dns_tcp_req_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+	struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+			struct dns_tcp_connection);
+	struct dns_tcp_call *call;
+	
+	WERROR err;
+	
+	if (!W_ERROR_IS_OK(err)) {
+		DEBUG(1, ("dns_req_done error: %s\n", win_errstr(err)));
+		return;
+	}
+	
+	TALLOC_FREE(subreq);
+	tevent_req_done(req);
+}
+
+/*  receiver */
+int dns_tcp_req_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;
+
+	/* tevent_req_is_unix_error defined in tevent_unix.h */
+	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;
+}
+
+/*** TSIG generation ***/
+
+/* identify tkey in record */
+struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+				        const char *name)
+{
+	struct dns_client_tkey *tkey = NULL;
+	uint16_t i = 0;
+
+	do {
+		struct dns_client_tkey *tmp_key = store->tkeys[i];
+
+		i++;
+		i %= TKEY_BUFFER_SIZE;
+
+		if (tmp_key == NULL) {
+			continue;
+		}
+		if (strcmp(name, tmp_key->name) == 0) {
+			tkey = tmp_key;
+			break;
+		}
+	} while (i != 0);
+
+	return tkey;
+}
+
+/* generate signature and rebuild packet with TSIG */
+WERROR dns_cli_generate_tsig(struct dns_client *dns,
+		       		TALLOC_CTX *mem_ctx,
+		       		struct dns_request_cli_state *state,
+		   			struct dns_name_packet *packet,
+	      			DATA_BLOB *in)
+{
+	int tsig_flag = 0;
+	struct dns_client_tkey *tkey = NULL;
+	uint16_t i, arcount = 0;
+	DATA_BLOB tsig_blob, fake_tsig_blob;
+	uint8_t *buffer = NULL;
+	size_t buffer_len = 0, packet_len = 0;
+	
+	NTSTATUS gen_sig;
+	DATA_BLOB sig = (DATA_BLOB) {.data = NULL, .length = 0};
+	struct dns_res_rec *tsig = NULL;
+	time_t current_time = time(NULL);
+
+	/* find TSIG record in inbound packet */
+	for (i=0; i < packet->arcount; i++) {
+		if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) {
+			tsig_flag = 1;
+			break;
+		}
+	}
+	if (tsig_flag != 1) {
+		return WERR_OK;
+	}
+
+	/* check TSIG record format consistency */
+	if (tsig_flag == 1 && i + 1 != packet->arcount) {
+		DEBUG(1, ("TSIG format inconsistent!\n"));
+		return DNS_ERR(FORMAT_ERROR);
+	}
+
+	/* save the keyname from the TSIG request to add MAC later */
+	tkey = dns_find_cli_tkey(dns->tkeys, state->tsig->name);
+	if (tkey == NULL) {
+		state->key_name = talloc_strdup(state->mem_ctx,
+						state->tsig->name);
+		if (state->key_name == NULL) {
+			return WERR_NOT_ENOUGH_MEMORY;
+		}
+		state->tsig_error = DNS_RCODE_BADKEY;
+		return DNS_ERR(NOTAUTH);
+	}
+	state->key_name = talloc_strdup(state->mem_ctx, tkey->name);
+	if (state->key_name == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+
+	/* 
+	 * preserve input packet but remove TSIG record bytes
+	 * then count down the arcount field in the packet 
+	 */
+	packet_len = in->length - tsig_blob.length;
+	packet->arcount--;
+	arcount = RSVAL(buffer, 10);
+	RSSVAL(buffer, 10, arcount-1);
+
+	/* append fake_tsig_blob to buffer */
+	buffer_len = packet_len + fake_tsig_blob.length;
+	buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
+	if (buffer == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+	
+	memcpy(buffer, in->data, packet_len);
+	memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length);
+
+	/* generate signature */
+	gen_sig = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
+				    buffer, buffer_len, &sig);
+
+	/* get MAC size and save MAC to sig*/
+	sig.length = state->tsig->rdata.tsig_record.mac_size;
+	sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
+	if (sig.data == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+
+	/* rebuild packet with MAC from gensec_sign_packet() */
+	tsig = talloc_zero(mem_ctx, struct dns_res_rec);
+
+	tsig->name = talloc_strdup(tsig, state->key_name);
+	if (tsig->name == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+	tsig->rr_class = DNS_QCLASS_ANY;
+	tsig->rr_type = DNS_QTYPE_TSIG;
+	tsig->ttl = 0;
+	tsig->length = UINT16_MAX;
+	tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig");
+	tsig->rdata.tsig_record.time_prefix = 0;
+	tsig->rdata.tsig_record.time = current_time;
+	tsig->rdata.tsig_record.fudge = 300;
+	tsig->rdata.tsig_record.error = state->tsig_error;
+	tsig->rdata.tsig_record.original_id = packet->id;
+	tsig->rdata.tsig_record.other_size = 0;
+	tsig->rdata.tsig_record.other_data = NULL;
+	if (sig.length > 0) {
+		tsig->rdata.tsig_record.mac_size = sig.length;
+		tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length);
+	}
+	
+	packet->additional = talloc_realloc(mem_ctx, packet->additional,
+					    struct dns_res_rec,
+					    packet->arcount + 1);
+	if (packet->additional == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+	packet->arcount++;
+	
+	return WERR_OK;
+}
\ No newline at end of file
-- 
2.7.4


From 3538e38751deb01942aa49e96fcd69e05688762a Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:13:09 +0300
Subject: [PATCH 03/23] libcli/dns/libtcp: TCP definitions for cli_dns

---
 libcli/dns/libtcp.h | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 libcli/dns/libtcp.h

diff --git a/libcli/dns/libtcp.h b/libcli/dns/libtcp.h
new file mode 100644
index 0000000..50cc9ec
--- /dev/null
+++ b/libcli/dns/libtcp.h
@@ -0,0 +1,94 @@
+/* TCP client-side DNS structures.
+ * 
+ * Copyright (C) 2017 Dimitrios Gravanis
+ * 
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ * 
+ * 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 __LIBTCP_H__
+#define __LIBTCP_H__
+
+#include "source4/dns_server/dns_server.h"
+#include "source4/dns_server/dnsserver_common.h"
+
+
+/** DNS TCP definitions **/
+struct tsocket_address;
+
+struct dns_socket {
+	struct dns_server *dns;
+	struct tsocket_address *local_address;
+};
+
+struct dns_tcp_request_state {
+	struct tevent_context *ev;
+	struct tstream_context **tstream;
+	size_t v_count;
+	uint32_t *reply;
+	size_t reply_len;
+};
+
+struct dns_tcp_connection {
+	struct stream_connection *conn;
+	struct dns_socket *dns_socket;
+	struct tstream_context *tstream;
+	struct tevent_queue *send_queue;
+};
+
+struct dns_tcp_call {
+	struct dns_tcp_connection *dns_conn;
+	DATA_BLOB in;
+	DATA_BLOB out;
+	uint8_t out_hdr[4];
+	struct iovec out_iov[2];
+};
+
+/** DNS TCP functions **/
+
+/* Send an DNS request to a DNS server via TCP
+ *
+ *@param mem_ctx        	talloc memory context to use
+ *@param ev             	tevent context to use
+ *@param server_addr_string address of the server as a string
+ *@param query          	dns query to send
+ *@param count 				length of the iovector
+ *@return tevent_req with the active request or NULL on out-of-memory
+ */
+struct tevent_req *dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					const char *server_addr_string,
+					struct iovec *vector,
+					size_t count);
+
+/* Receive the DNS response from the DNS server via TCP
+ *
+ *@param req       tevent_req struct returned from dns_request_send
+ *@param mem_ctx   talloc memory context to use for the reply string
+ *@param reply     buffer that will be allocated and filled with the dns reply
+ *@param reply_len length of the reply buffer
+ *@return 0/errno
+ */
+int dns_tcp_req_recv(struct tevent_req *req,
+			 TALLOC_CTX *mem_ctx,
+			 uint8_t **reply,
+			 size_t *reply_len);
+
+/* Callbacks */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq);
+void dns_tcp_req_done(struct tevent_req *subreq);
+
+#endif /*__LIBTCP_H__*/
\ No newline at end of file
-- 
2.7.4


From 0ca1174209c9393ca01febb158b93faad73298d8 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:13:54 +0300
Subject: [PATCH 04/23] libcli/dns/libudp: UDP definitions for cli_dns

---
 libcli/dns/libudp.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 libcli/dns/libudp.h

diff --git a/libcli/dns/libudp.h b/libcli/dns/libudp.h
new file mode 100644
index 0000000..57ab85f
--- /dev/null
+++ b/libcli/dns/libudp.h
@@ -0,0 +1,53 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Small async DNS library for Samba with socketwrapper support
+
+   Copyright (C) 2012 Kai Blin  <kai at 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 __LIBUDP_H__
+#define __LIBUDP_H__
+
+/** Send an dns request to a dns server using UDP
+ *
+ *@param mem_ctx        talloc memory context to use
+ *@param ev             tevent context to use
+ *@param server_address address of the server as a string
+ *@param query          dns query to send
+ *@param query_len      length of the query
+ *@return tevent_req with the active request or NULL on out-of-memory
+ */
+struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					const char *server_address,
+					const uint8_t *query,
+					size_t query_len);
+
+/** Get the dns response from a dns server via UDP
+ *
+ *@param req       tevent_req struct returned from dns_request_send
+ *@param mem_ctx   talloc memory context to use for the reply string
+ *@param reply     buffer that will be allocated and filled with the dns reply
+ *@param reply_len length of the reply buffer
+ *@return 0/errno
+ */
+int dns_udp_request_recv(struct tevent_req *req,
+			 TALLOC_CTX *mem_ctx,
+			 uint8_t **reply,
+			 size_t *reply_len);
+
+#endif /*__LIBUDP_H__*/
-- 
2.7.4


From 073aca58f436d3b152a21a98e9a20bda61ae0f86 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:14:26 +0300
Subject: [PATCH 05/23] libcli/dns/libtsig: GSS-TSIG definitions for cli_dns

---
 libcli/dns/libtsig.h | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)
 create mode 100644 libcli/dns/libtsig.h

diff --git a/libcli/dns/libtsig.h b/libcli/dns/libtsig.h
new file mode 100644
index 0000000..0f7ec2f
--- /dev/null
+++ b/libcli/dns/libtsig.h
@@ -0,0 +1,114 @@
+/* GSS-TSIG client-side DNS structures and utilites.
+ * 
+ * Copyright (C) 2017 Dimitrios Gravanis
+ * 
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ * 
+ * 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 __LIBTSIG_H__
+#define __LIBTSIG_H__
+
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+
+
+/** error definitions **/
+uint8_t werr_to_dns_err(WERROR werr);
+#define DNS_ERR(err_str) WERR_DNS_ERROR_RCODE_##err_str
+
+/** client structures **/
+struct dns_client_zone {
+	struct dns_client_zone *prev, *next;
+	const char *name;
+	struct ldb_dn *dn;
+};
+
+struct dns_client {
+	struct task_server *task;
+	struct ldb_context *samdb;
+	struct dns_client_zone *zones;
+	struct dns_client_tkey_store *tkeys;
+	struct cli_credentials *client_credentials;
+	uint16_t max_payload;
+};
+
+struct dns_request_cli_state {
+	TALLOC_CTX *mem_ctx;
+	uint16_t flags;
+	bool authenticated;
+	bool sign;
+	char *key_name;
+	struct dns_res_rec *tsig;
+	uint16_t tsig_error;
+};
+
+/** transaction key definitions **/
+#define TKEY_BUFFER_SIZE 128
+
+struct dns_client_tkey {
+	const char *name;
+	enum dns_tkey_mode mode;
+	const char *algorithm;
+	struct auth_session_info *session_info;
+	struct gensec_security *gensec;
+	bool complete;
+};
+
+struct dns_client_tkey_store {
+	struct dns_client_tkey **tkeys;
+	uint16_t next_idx;
+	uint16_t size;
+};
+
+/** functions **/
+
+/* Search for DNS key name in record to the expected name
+ *
+ *@param store 	dns_client_tkey_store to use for name search
+ *@param name   name to match
+ *@return tkey
+ */
+struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+				      const char *name);
+
+/* Make a record copy with empty TSIG rdata
+ *
+ *@param mem_ctx        	talloc memory context to use
+ *@param orig_record       	dns_res_rec struct to duplicate
+ *@param empty_record		dns_res_rec struct with empty RDATA
+ *@return WERR_OK/WERR_NOT_ENOUGH_MEMORY
+ */
+WERROR dns_empty_tsig(TALLOC_CTX *mem_ctx,
+					struct dns_res_rec *orig_record,
+					struct dns_res_rec *empty_record);
+
+/* Sign packet and rebuild with TSIG
+ *
+ *@param dns 		dns_client structure with client internals
+ *@param mem_ctx 	talloc memory context to use
+ *@param packet 	dns_name_packet that is used
+ *@param state 		packet state
+ *@param in 		data and length of packet
+ *@return WERR_OK/_NOT_ENOUGH_MEMORY/_FORMAT_ERROR/_NOTAUTH
+ */
+WERROR dns_cli_generate_sig(struct dns_client *dns,
+		       TALLOC_CTX *mem_ctx,
+		       struct dns_name_packet *packet,
+		       struct dns_request_cli_state *state,
+		       DATA_BLOB *in);
+
+#endif /* __LIBTSIG_H__ */
\ No newline at end of file
-- 
2.7.4


From b38eb95bda4b232abb422bdf570136361ee70ab6 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:15:16 +0300
Subject: [PATCH 06/23] libcli/dns/wrap_cli: provides wrap functionality for
 cli_dns

---
 libcli/dns/wrap_cli.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 libcli/dns/wrap_cli.c

diff --git a/libcli/dns/wrap_cli.c b/libcli/dns/wrap_cli.c
new file mode 100644
index 0000000..f82ad91
--- /dev/null
+++ b/libcli/dns/wrap_cli.c
@@ -0,0 +1,83 @@
+/* DNS UDP/TCP send/recv wrapping with TSIG generation.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ * 
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ * 
+ * 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 "libcli/dns/libwrap.h"
+
+/* wrap dns udp/tcp req send/recv() and tsig generation functions
+ * see libcli/dns/lib*.h for wrapped function declarations
+ */
+
+/* udp */
+struct tevent_req *__wrap_udp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+			const char *server_addr_string, const uint8_t *query, size_t query_len)
+{
+	return 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);
+}
+
+int __wrap_udp_req_recv(struct tevent_req *subreq, struct tevent_req *req,
+			TALLOC_CTX *mem_ctx, uint8_t **reply, size_t *reply_len)
+{
+	void dns_udp_request_get_reply(tevent_req *subreq);
+
+	void dns_udp_request_done(tevent_req *subreq);
+
+	return dns_udp_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+				uint8_t **reply, size_t *reply_len);
+}
+
+/* tcp */
+struct tevent_req *__wrap_tcp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+			const char *server_addr_string, struct iovec *vector, size_t count)
+{
+	return dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					const char *server_addr_string,
+					struct iovec *vector,
+					size_t count);
+}
+
+int __wrap_tcp_req_recv(struct tevent_req *subreq, struct tevent_req *req,
+			TALLOC_CTX *mem_ctx, uint8_t **reply, size_t *reply_len)
+{
+	void dns_tcp_req_recv_reply(tevent_req *subreq);
+
+	void dns_tcp_req_done(tevent_req *subreq);
+
+	return dns_tcp_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, 
+				uint8_t **reply, size_t *reply_len);
+}
+
+/* tsig gen */
+WERROR __wrap_tcp_cli_tsig_gen(struct dns_client_tkey_store *store, const char *name,
+	   struct dns_client *dns, TALLOC_CTX *mem_ctx, struct dns_request_state *state, 
+	   struct dns_name_packet *packet, DATA_BLOB *in)
+{
+	struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+				    const char *name);
+
+	return dns_cli_generate_tsig(struct dns_client *dns, TALLOC_CTX *mem_ctx,
+		       		struct dns_request_state *state, struct dns_name_packet *packet,
+		        	DATA_BLOB *in);
+}
\ No newline at end of file
-- 
2.7.4


From abb0aa0ba8c8364c54b298a24bc47cf5bd189557 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:15:58 +0300
Subject: [PATCH 07/23] libcli/dns/libwrap: wrapper definitions for wrap_cli

---
 libcli/dns/libwrap.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 libcli/dns/libwrap.h

diff --git a/libcli/dns/libwrap.h b/libcli/dns/libwrap.h
new file mode 100644
index 0000000..071d55c
--- /dev/null
+++ b/libcli/dns/libwrap.h
@@ -0,0 +1,44 @@
+/* DNS UDP/TCP send/recv wrap library with TSIG generation.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ * 
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ * 
+ * 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 __LIBWRAP_H__
+#define __LIBWRAP_H__
+
+/* udp */
+struct tevent_req *udp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+			const char *server_addr_string, const uint8_t *query, size_t query_len);
+
+int udp_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+	uint8_t **reply, size_t *reply_len);
+
+/* tcp */
+struct tevent_req *tcp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+			const char *server_addr_string, struct iovec *vector, size_t count);
+
+int tcp_req_recv(struct tevent_req *subreq, struct tevent_req *req,
+	TALLOC_CTX *mem_ctx, uint8_t **reply, size_t *reply_len);
+
+/* tsig gen */
+WERROR tcp_cli_tsig_gen(struct dns_client_tkey_store *store, const char *name,
+	   struct dns_client *dns, TALLOC_CTX *mem_ctx,v struct dns_request_state *state, 
+	   struct dns_name_packet *packet,	DATA_BLOB *in);
+
+#endif /* __LIBWRAP_H__ */
\ No newline at end of file
-- 
2.7.4


From 2e12732efc492ffe6e3cb09b78feaab6d6819cb2 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:17:10 +0300
Subject: [PATCH 08/23] cli-fn/client_crypto: GSS-TSIG signing feature library

---
 libcli/dns/cli-fn/client_crypto.c | 201 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)
 create mode 100644 libcli/dns/cli-fn/client_crypto.c

diff --git a/libcli/dns/cli-fn/client_crypto.c b/libcli/dns/cli-fn/client_crypto.c
new file mode 100644
index 0000000..5976af8
--- /dev/null
+++ b/libcli/dns/cli-fn/client_crypto.c
@@ -0,0 +1,201 @@
+/* GSS-TSIG client-side handling for signed packets.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ * 
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ * 
+ * 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 "includes.h"
+#include "lib/crypto/hmacmd5.h"
+#include "libcli/util/ntstatus.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "source4/dns_server/dns_server.h"
+#include "libcli/dns/libtsig.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
+
+/* 
+ * make a copy of the original tsig record
+ * with null rdata values
+ */
+static WERROR dns_empty_tsig(TALLOC_CTX *mem_ctx,
+					struct dns_res_rec *orig_record,
+					struct dns_res_rec *empty_record)
+{
+	/* see /libprc/idl/dns.idl for PIDL tsig definition */
+	empty_record->name = talloc_strdup(mem_ctx, orig_record->name);
+	W_ERROR_HAVE_NO_MEMORY(empty_record->name);
+	empty_record->rr_type = orig_record->rr_type;
+	empty_record->rr_class = orig_record->rr_class;
+	empty_record->ttl = orig_record->ttl;
+	empty_record->length = orig_record->length;
+	
+	/* empty tsig rdata field in the new record */
+	/* the smooth way! */
+	empty_record->rdata.tsig_record.algorithm_name = talloc_strdup(mem_ctx, 
+							orig_record->rdata.tsig_record.algorithm_name);
+	W_ERROR_HAVE_NO_MEMORY(empty_record->rdata.tsig_record.algorithm_name);
+	ZERO_STRUCT(empty_record->rdata.tsig_record);
+
+	return WERR_OK;
+}
+
+/* identify tkey in record */
+struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+				        const char *name)
+{
+	struct dns_client_tkey *tkey = NULL;
+	uint16_t i = 0;
+
+	do {
+		struct dns_client_tkey *tmp_key = store->tkeys[i];
+
+		i++;
+		i %= TKEY_BUFFER_SIZE;
+
+		if (tmp_key == NULL) {
+			continue;
+		}
+		if (strcmp(name, tmp_key->name) == 0) {
+			tkey = tmp_key;
+			break;
+		}
+	} while (i != 0);
+
+	return tkey;
+}
+
+/* generate signature and rebuild packet with TSIG */
+WERROR dns_cli_generate_tsig(struct dns_client *dns,
+		       		TALLOC_CTX *mem_ctx,
+		       		struct dns_request_cli_state *state,
+		   			struct dns_name_packet *packet,
+	      			DATA_BLOB *in)
+{
+	int tsig_flag = 0;
+	struct dns_client_tkey *tkey = NULL;
+	uint16_t i, arcount = 0;
+	DATA_BLOB tsig_blob, fake_tsig_blob;
+	uint8_t *buffer = NULL;
+	size_t buffer_len = 0, packet_len = 0;
+	
+	NTSTATUS gen_sig;
+	DATA_BLOB sig = (DATA_BLOB) {.data = NULL, .length = 0};
+	struct dns_res_rec *tsig = NULL;
+	time_t current_time = time(NULL);
+
+	/* find TSIG record in inbound packet */
+	for (i=0; i < packet->arcount; i++) {
+		if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) {
+			tsig_flag = 1;
+			break;
+		}
+	}
+	if (tsig_flag != 1) {
+		return WERR_OK;
+	}
+
+	/* check TSIG record format consistency */
+	if (tsig_flag == 1 && i + 1 != packet->arcount) {
+		DEBUG(1, ("TSIG format inconsistent!\n"));
+		return DNS_ERR(FORMAT_ERROR);
+	}
+
+	/* save the keyname from the TSIG request to add MAC later */
+	tkey = dns_find_cli_tkey(dns->tkeys, state->tsig->name);
+	if (tkey == NULL) {
+		state->key_name = talloc_strdup(state->mem_ctx,
+						state->tsig->name);
+		if (state->key_name == NULL) {
+			return WERR_NOT_ENOUGH_MEMORY;
+		}
+		state->tsig_error = DNS_RCODE_BADKEY;
+		return DNS_ERR(NOTAUTH);
+	}
+	state->key_name = talloc_strdup(state->mem_ctx, tkey->name);
+	if (state->key_name == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+
+	/* 
+	 * preserve input packet but remove TSIG record bytes
+	 * then count down the arcount field in the packet 
+	 */
+	packet_len = in->length - tsig_blob.length;
+	packet->arcount--;
+	arcount = RSVAL(buffer, 10);
+	RSSVAL(buffer, 10, arcount-1);
+
+	/* append fake_tsig_blob to buffer */
+	buffer_len = packet_len + fake_tsig_blob.length;
+	buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
+	if (buffer == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+	
+	memcpy(buffer, in->data, packet_len);
+	memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length);
+
+	/* generate signature */
+	gen_sig = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
+				    buffer, buffer_len, &sig);
+
+	/* get MAC size and save MAC to sig*/
+	sig.length = state->tsig->rdata.tsig_record.mac_size;
+	sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
+	if (sig.data == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+
+	/* rebuild packet with MAC from gensec_sign_packet() */
+	tsig = talloc_zero(mem_ctx, struct dns_res_rec);
+
+	tsig->name = talloc_strdup(tsig, state->key_name);
+	if (tsig->name == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+	tsig->rr_class = DNS_QCLASS_ANY;
+	tsig->rr_type = DNS_QTYPE_TSIG;
+	tsig->ttl = 0;
+	tsig->length = UINT16_MAX;
+	tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig");
+	tsig->rdata.tsig_record.time_prefix = 0;
+	tsig->rdata.tsig_record.time = current_time;
+	tsig->rdata.tsig_record.fudge = 300;
+	tsig->rdata.tsig_record.error = state->tsig_error;
+	tsig->rdata.tsig_record.original_id = packet->id;
+	tsig->rdata.tsig_record.other_size = 0;
+	tsig->rdata.tsig_record.other_data = NULL;
+	if (sig.length > 0) {
+		tsig->rdata.tsig_record.mac_size = sig.length;
+		tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length);
+	}
+	
+	packet->additional = talloc_realloc(mem_ctx, packet->additional,
+					    struct dns_res_rec,
+					    packet->arcount + 1);
+	if (packet->additional == NULL) {
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+	packet->arcount++;
+	
+	return WERR_OK;
+}
-- 
2.7.4


From d0fdff708f2cca609dc5b5a273e74423d7ee5bd3 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:17:43 +0300
Subject: [PATCH 09/23] cli-fn/dns_tcp: TCP send/recv request feature library

---
 libcli/dns/cli-fn/dns_tcp.c | 221 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 221 insertions(+)
 create mode 100644 libcli/dns/cli-fn/dns_tcp.c

diff --git a/libcli/dns/cli-fn/dns_tcp.c b/libcli/dns/cli-fn/dns_tcp.c
new file mode 100644
index 0000000..4e94e0e
--- /dev/null
+++ b/libcli/dns/cli-fn/dns_tcp.c
@@ -0,0 +1,221 @@
+/* TCP client-side DNS call handling.
+ * 
+ * Copyright (C) 2017 Dimitrios Gravanis
+ * 
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014
+ * 
+ * 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/util/tstream.h"
+#include "source4/smbd/service_task.h"
+#include "source4/smbd/service_stream.h"
+#include "source4/lib/stream/packet.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "libcli/dns/libtcp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+
+#define DNS_REQUEST_TIMEOUT 2
+
+/*** TCP Requests ***/
+
+/* TCP callbacks */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq);
+void dns_tcp_req_done(struct tevent_req *subreq);
+
+/* tcp request to send */
+struct tevent_req *dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+					struct tevent_context *ev,
+					const char *server_addr_string,
+					struct iovec *vector,
+					size_t count)
+{
+	struct tevent_req *req, *subreq, *socreq;
+	struct dns_tcp_request_state *state;
+	struct tsocket_address *local_address, *remote_address;
+	struct tstream_context *stream;
+	int req_ret, soc_ret, err;
+
+	req = tevent_req_create(mem_ctx, &state, struct dns_tcp_request_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	state->ev = ev;
+
+	/* check for connected sockets and use if any */
+	req_ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+						&local_address);
+	if (req_ret != 0) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	req_ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+						DNS_SERVICE_PORT, &remote_address);
+	if (req_ret != 0) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+
+	/* must be reviewed! */
+	soc_ret = tstream_inet_tcp_connect_recv(socreq, err, mem_ctx, stream, NULL);
+	TALLOC_FREE(socreq);
+	if (soc_ret == -1 && err != 0) {
+		tevent_req_error(socreq, err);
+		return tevent_req_post(req, ev);
+	}
+
+	socreq = tstream_inet_tcp_connect_send(mem_ctx, ev, local_address, remote_address);
+	if (tevent_req_nomem(socreq, req)) {
+		tevent_req_error(req, errno);
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(socreq, dns_tcp_req_send, req);
+
+	state->tstream = stream;
+	state->v_count = count;
+
+	subreq = tstream_writev_send(mem_ctx, ev, stream, vector, count);
+	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))) {
+		tevent_req_oom(req);
+		return tevent_req_post(req, ev);
+	}
+
+	/* associate callback */
+	tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, req);
+	
+	return req;
+}
+
+/* get buffer and wait to receive server response */
+void dns_tcp_req_recv_reply(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);
+	ssize_t stream_len;
+	int err = 0;
+	NTSTATUS status;
+
+	stream_len = tstream_writev_recv(subreq, &err);
+	TALLOC_FREE(subreq);
+
+	if (stream_len == -1 && err != 0) {
+		tevent_req_error(req, err);
+		return;
+	}
+
+	if (stream_len != state->v_count) {
+		tevent_req_error(req, EIO);
+		return;
+	}
+
+	/* response loop */
+	struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+			struct dns_tcp_connection);
+	struct tsocket_address *local_address, *server_address;
+	struct dns_client *dns = dns_conn->dns_socket->dns; // uses server iface
+	struct dns_tcp_call *call;
+
+	call = talloc(dns_conn, struct dns_tcp_call);
+	if (call == NULL) {
+		DEBUG(1, ("dns_tcp_req_recv_reply: NULL call\n"));
+		return;
+	}
+	call->dns_conn = dns_conn;
+
+	status = tstream_read_pdu_blob_recv(subreq, call, &call->in);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(1, ("tstream_read_pdu_blob_recv: error %s\n", nt_errstr(status)));
+		return;
+	}
+	
+	if (subreq == NULL) {
+		DEBUG(1, ("dns_tcp_req_recv_reply: NULL subreq\n"));
+		return;
+	}
+	tevent_req_set_callback(subreq, dns_tcp_req_done, call);
+	TALLOC_FREE(subreq);
+	
+	subreq = tstream_read_pdu_blob_send(dns_conn,
+					    dns_conn->conn->event.ctx,
+					    dns_conn->tstream,
+					    2, 
+					    packet_full_request_u16,
+					    dns_conn);
+	/* loop callback */
+	tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, dns_conn);
+}
+
+/* callback status */
+void dns_tcp_req_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+	struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+			struct dns_tcp_connection);
+	struct dns_tcp_call *call;
+	
+	WERROR err;
+	
+	if (!W_ERROR_IS_OK(err)) {
+		DEBUG(1, ("dns_req_done error: %s\n", win_errstr(err)));
+		return;
+	}
+	
+	TALLOC_FREE(subreq);
+	tevent_req_done(req);
+}
+
+/*  receiver */
+int dns_tcp_req_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;
+
+	/* tevent_req_is_unix_error defined in tevent_unix.h */
+	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;
+}
\ No newline at end of file
-- 
2.7.4


From a01c0c1dbeca73e4bbab08d3055a30b8dfceed3e Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:18:13 +0300
Subject: [PATCH 10/23] cli-fn/dns_udp: UDP send/recv request feature library

---
 libcli/dns/cli-fn/dns_udp.c | 178 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 libcli/dns/cli-fn/dns_udp.c

diff --git a/libcli/dns/cli-fn/dns_udp.c b/libcli/dns/cli-fn/dns_udp.c
new file mode 100644
index 0000000..f8a5495
--- /dev/null
+++ b/libcli/dns/cli-fn/dns_udp.c
@@ -0,0 +1,178 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Small async DNS library for Samba with socketwrapper support
+
+   Copyright (C) 2010 Kai Blin  <kai at 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/libudp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+#include "librpc/gen_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 2
+
+/* Declare callback functions used below. */
+void dns_udp_request_get_reply(struct tevent_req *subreq);
+void dns_udp_request_done(struct tevent_req *subreq);
+
+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_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))) {
+		tevent_req_oom(req);
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_set_callback(subreq, dns_udp_request_get_reply, req);
+	return req;
+}
+
+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);
+}
+
+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);
+}
+
+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;
+}
-- 
2.7.4


From b58d2eed4552646491c45ab05e860fe69b1dc2aa Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:18:54 +0300
Subject: [PATCH 11/23] cli-fn/README: feature libraries information

---
 libcli/dns/cli-fn/README.md | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 libcli/dns/cli-fn/README.md

diff --git a/libcli/dns/cli-fn/README.md b/libcli/dns/cli-fn/README.md
new file mode 100644
index 0000000..bdd66ba
--- /dev/null
+++ b/libcli/dns/cli-fn/README.md
@@ -0,0 +1,17 @@
+## README: features
+
+The individual function libraries that are incorporated in cli_dns.c, 
+to provide client-side DNS call features. Each library comes with a 
+corresponding test suite in libcli/dns/cmocka-tests/ directory.
+
+Descriptions
+
+* client_crypto.c: GSS-TSIG client-side handling for signed packets
+* dns_tcp.c: TCP client-side DNS call handling
+* dns_udp.c: Small async DNS library with socketwrapper support
+
+It is highly recommended that the above libraries will be used for 
+adding and testing features in  libcli/dns individually, **BEFORE** 
+implementing any changes in libcli/cli_dns.c library.
+
+*Associated headers are found in libcli/dns/*
-- 
2.7.4


From a4d01bcb5d7f8a39621f96e089cd0ab25234da83 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:19:47 +0300
Subject: [PATCH 12/23] cmocka-tests/cli_tests: test suite for cli_dns using
 Cmocka

---
 libcli/dns/cmocka-tests/cli_tests.c | 305 ++++++++++++++++++++++++++++++++++++
 1 file changed, 305 insertions(+)
 create mode 100644 libcli/dns/cmocka-tests/cli_tests.c

diff --git a/libcli/dns/cmocka-tests/cli_tests.c b/libcli/dns/cmocka-tests/cli_tests.c
new file mode 100644
index 0000000..090b953
--- /dev/null
+++ b/libcli/dns/cmocka-tests/cli_tests.c
@@ -0,0 +1,305 @@
+/* Unix SMB/CIFS implementation.
+ * 
+ * Test suite for:
+ * DNS UDP/TCP call handler with socketwrapper support and TSIG generation
+ *
+ * Copyright 2017 (c) Dimitrios Gravanis
+ *
+ * Uses cmocka C testing API.
+ * Copyright 2013 (c) Andreas Schneider <asn at cynapses.org>
+ *                    Jakub Hrozek <jakub.hrozek at gmail.com>
+ *
+ * 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/>.
+ */
+
+/* test requirements */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "libcli/dns/cli_dns.c"
+
+/* TSIG generation */
+#include "includes.h"
+#include "lib/crypto/hmacmd5.h"
+#include "libcli/util/ntstatus.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "source4/dns_server/dns_server.h"
+#include "libcli/dns/libtsig.h"
+
+/* DNS call send/recv() */
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "source4/smbd/service_task.h"
+#include "source4/smbd/service_stream.h"
+#include "source4/lib/stream/packet.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "libcli/dns/libtcp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+
+#define DNS_REQUEST_TIMEOUT 2
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
+
+
+/** test tcp send/recv functionality **/
+
+/* calls fail() if TCP test_req is NULL */
+static void test_req_send(void **state)
+{
+	/* incomplete */
+	TALLOC_CTX *mem_ctx;
+	struct tevent_context *test_ev;
+	const char *test_server_addr_string = "TEST_SRVR_ADDR";
+	struct iovec *test_vector;
+	size_t test_count = SIZE_MAX;
+
+	struct tevent_req *test_req = dns_tcp_req_send(mem_ctx, test_ev,
+			test_server_addr_string, test_vector, test_count);
+
+	assert_non_null(test_req);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_req_recv_reply(void **state)
+{
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_tcp_req_recv_reply(test_subreq);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_req_done(void **state)
+{
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_tcp_req_done(test_subreq);
+	return;
+}
+
+/* calls fail() if test_rcv is not 0 */
+static void test_req_recv(void **state)
+{
+	TALLOC_CTX *mem_ctx;
+	struct tevent_req *test_req;
+	uint8_t **test_reply = UINT8_MAX;
+	size_t *test_reply_len = SIZE_MAX;
+
+	int test_rcv = dns_tcp_req_recv(test_req, mem_ctx, test_reply, test_reply_len);
+	
+	assert_int_equal(test_rcv, 0);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/** test udp send/recv functionality **/
+
+/* calls fail() if UDP test_req is NULL */
+static void test_request_send(void **state)
+{
+	TALLOC_CTX *mem_ctx;
+	struct tevent_context *test_ev;
+	const char *test_server_addr_string = "TEST_SRVR_ADDR";
+	const uint8_t *test_query = UINT8_MAX;
+	size_t test_query_len = SIZE_MAX;
+
+	struct tevent_req *test_req = dns_udp_request_send(mem_ctx, test_ev,
+			test_server_addr_string, test_query, test_query_len);
+	
+	assert_non_null(test_req);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_request_get_reply(void **state)
+{
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_udp_request_get_reply(test_subreq);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_request_done(void **state)
+{
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_udp_request_done(test_subreq);
+	return;
+}
+
+/* calls fail() if test_rcv is not 0 */
+static void test_request_recv(void **state)
+{
+	struct tevent_req *test_req;
+	TALLOC_CTX *mem_ctx;
+	uint8_t **test_reply = UINT8_MAX;
+	size_t *test_reply_len = SIZE_MAX;
+
+	int test_rcv = dns_udp_request_recv(test_req, mem_ctx, test_reply, test_reply_len);
+	
+	assert_int_equal(test_rcv, 0);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/** test gss-tsig functionality **/
+
+/* helper struct functions */
+static struct dns_res_rec *test_record(void) {
+
+	TALLOC_CTX *mem_ctx;
+	struct dns_res_rec *test_rec;
+	test_rec->name = "TEST_RECORD";
+	test_rec->rr_type = DNS_QTYPE_TSIG;
+	test_rec->rr_class = DNS_QCLASS_ANY;
+	test_rec->ttl = 0;
+	test_rec->length = UINT16_MAX;
+	/* rdata */
+	test_rec->rdata.tsig_record.algorithm_name = "gss-tsig";
+	test_rec->rdata.tsig_record.time_prefix = 0;
+	test_rec->rdata.tsig_record.time = 0;
+	test_rec->rdata.tsig_record.fudge = 300;
+	test_rec->rdata.tsig_record.mac_size = UINT16_MAX;
+	test_rec->rdata.tsig_record.mac = NULL;
+	test_rec->rdata.tsig_record.original_id = UINT16_MAX;
+	test_rec->rdata.tsig_record.error = UINT16_MAX;
+	test_rec->rdata.tsig_record.other_size = 0;
+	test_rec->rdata.tsig_record.other_data = NULL;
+
+	return test_rec;
+};
+
+static struct dns_client_tkey *test_tkey_name(void) {
+	
+	struct dns_client_tkey *test_tkey = NULL;
+	test_tkey->name = "TEST_TKEY";
+
+	return test_tkey;
+};
+
+/* calls fail() if assertions are false */
+static void tkey_test(void **state)
+{
+	struct dns_client_tkey_store *test_store;
+	const char *test_name = "TEST_TKEY";
+	
+	struct dns_client_tkey *testing;
+	struct dns_client_tkey *verifier;
+
+	testing = test_tkey_name();
+	verifier  = dns_find_cli_tkey(test_store, test_name);
+
+	assert_non_null(testing);
+	assert_non_null(verifier);
+	assert_string_equal(testing->name, verifier->name);
+	
+	TALLOC_FREE(testing);
+	TALLOC_FREE(verifier);
+	return;
+}
+
+/* calls fail() if test_werr not in werr_set */
+static void gen_tsig_test(void **state)
+{
+	TALLOC_CTX *mem_ctx;
+	DATA_BLOB *in_test = {NULL, SIZE_MAX};
+	
+	struct dns_client *test_client;
+	test_client->samdb = NULL;
+	test_client->zones = NULL;
+	test_client->tkeys = NULL;
+	test_client->client_credentials = NULL;
+	test_client->max_payload = UINT16_MAX;
+	
+	struct dns_request_cli_state *test_state;
+	test_state->flags = UINT16_MAX;
+	test_state->authenticated = true;
+	test_state->sign = true;
+	test_state->key_name = "TKEY_NAME";
+	test_state->tsig->name = "TSIG_RECORD";
+	test_state->tsig->rr_type = DNS_QTYPE_TSIG;
+	test_state->tsig->rr_class = DNS_QCLASS_ANY;
+	test_state->tsig->ttl = 0;
+	test_state->tsig->length = UINT16_MAX;
+	test_state->tsig_error = UINT16_MAX;
+	
+	struct dns_name_packet *test_packet;
+	test_packet->id = UINT16_MAX;
+	test_packet->qdcount = UINT16_MAX;
+	test_packet->ancount = UINT16_MAX;
+	test_packet->nscount = UINT16_MAX;
+	test_packet->arcount = UINT16_MAX;
+
+	/* test error codes */
+	WERROR test_werr = dns_cli_generate_tsig(test_client, mem_ctx,
+								test_state, test_packet, in_test);
+
+	/* expected WERROR output */
+	assert_true(W_ERROR_IS_OK(test_werr));
+	assert_true(W_ERROR_EQUAL(WERR_NOT_ENOUGH_MEMORY, test_werr));
+	assert_true(W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), test_werr));
+	assert_true(W_ERROR_EQUAL(DNS_ERR(NOTAUTH), test_werr));
+
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* run test suite */
+int main(void) 
+{
+	/* test structure */
+	const struct CMUnitTest tests[] = {
+		/* tcp */
+		cmocka_unit_test(test_req_send),
+		cmocka_unit_test(test_req_recv_reply),
+		cmocka_unit_test(test_req_done),
+		cmocka_unit_test(test_req_recv),
+		/* udp */
+		cmocka_unit_test(test_request_send),
+		cmocka_unit_test(test_request_get_reply),
+		cmocka_unit_test(test_request_done),
+		cmocka_unit_test(test_request_recv),
+		/* gss-tsig */
+		cmocka_unit_test(tkey_test),
+		cmocka_unit_test(gen_tsig_test),
+	};
+
+	cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
\ No newline at end of file
-- 
2.7.4


From 5ab2b0943eaf60fee6502032f9c6d0b7c1d5e83f Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:20:30 +0300
Subject: [PATCH 13/23] cmocka-tests/wscript_build: integrate recursive build
 for cli_tests

---
 libcli/dns/cmocka-tests/wscript_build | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100755 libcli/dns/cmocka-tests/wscript_build

diff --git a/libcli/dns/cmocka-tests/wscript_build b/libcli/dns/cmocka-tests/wscript_build
new file mode 100755
index 0000000..8cf4219
--- /dev/null
+++ b/libcli/dns/cmocka-tests/wscript_build
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+# cli_dns test suite
+bld.SAMBA_BINARY('client_tests',
+		source='cli_tests.c',
+		deps='LIBTSOCKET tevent-util cmocka gensec auth samba_server_gensec dnsserver_common',
+		install=False)
-- 
2.7.4


From 2c4b9f5333bd2ea3a8374db598317687ed8cc68e Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:21:03 +0300
Subject: [PATCH 14/23] cmocka-tests/README: test suite information

---
 libcli/dns/cmocka-tests/README.md | 50 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 libcli/dns/cmocka-tests/README.md

diff --git a/libcli/dns/cmocka-tests/README.md b/libcli/dns/cmocka-tests/README.md
new file mode 100644
index 0000000..4c4a5b8
--- /dev/null
+++ b/libcli/dns/cmocka-tests/README.md
@@ -0,0 +1,50 @@
+## README: test suites
+
+
+*Test suite cli_tests.c is functional and currently incorporated in Samba/wscript/*
+
+*Individual test suites are functional, but stand alone waf built is not yet supported.*
+
+Tests for the client features are divided in four different test suites:
+
+*	cli_crypto_test: transaction key name search and GSS-TSIG signature generation
+*	dns_tcp_test: individual DNS TCP send/receive request packet test
+*	dns_udp_test: individual DNS UDP send/receive request packet test
+*	cli_tests: complete test suite for libcli/dns/cli_dns.c
+
+*See cli-fn for corresponding libraries*
+
+### Configure and Build complete test suite
+
+The Samba top-level wscript and wscript_build have been modified to recursively implement test suites in 
+Samba builds. Running `$ waf configure && waf` in Samba top-level directory, takes care of creating the 
+test executable and incorporating it during the building process.
+
+### Configure and Build individual test suites
+
+You can build and incorporate individual tests in Samba builds, by configuring Samba with the "ENABLE_SELFTEST" 
+option:
+
+`$ ./configure --enable-selftest`
+
+### Configure and Build individual test suites (standalone)
+
+Samba contributors, or anyone interested in the specific code, may wish to build the individual tests for 
+feature testing and/or other development purposes. To do so:
+
+In dns/cmocka-tests/test-fn/:
+```
+$ waf configure
+
+$ waf
+```
+*default build directory is set to cmocka-tests/build-tests*
+
+To clean "leftovers":
+
+```
+$ waf clean
+
+$ waf distclean
+```
+
-- 
2.7.4


From 21057db243a576e3bae217c302c66788ef101a15 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:22:03 +0300
Subject: [PATCH 15/23] cmocka-tests/test-fn/cli_crypto_test: individual tests
 for TSIG crypto feature

---
 libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c | 149 ++++++++++++++++++++++
 1 file changed, 149 insertions(+)
 create mode 100644 libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c

diff --git a/libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c b/libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c
new file mode 100644
index 0000000..412c6c1
--- /dev/null
+++ b/libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c
@@ -0,0 +1,149 @@
+/* Tests GSS-TSIG client-side handling for signed packets.
+ * 
+ * Copyright 2017 (c) Dimitrios Gravanis
+ *
+ * Uses cmocka C testing API.
+ * Copyright 2013 (c) Andreas Schneider <asn at cynapses.org>
+ *                    Jakub Hrozek <jakub.hrozek at gmail.com>
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "libcli/dns/cli-fn/client_crypto.c"
+
+
+/** test gss-tsig functionality **/
+
+/* helper struct functions */
+static struct dns_res_rec *test_record(void) {
+
+	TALLOC_CTX *mem_ctx;
+	struct dns_res_rec *test_rec;
+	test_rec->name = "TEST_RECORD";
+	test_rec->rr_type = DNS_QTYPE_TSIG;
+	test_rec->rr_class = DNS_QCLASS_ANY;
+	test_rec->ttl = 0;
+	test_rec->length = UINT16_MAX;
+	/* rdata */
+	test_rec->rdata.tsig_record.algorithm_name = "gss-tsig";
+	test_rec->rdata.tsig_record.time_prefix = 0;
+	test_rec->rdata.tsig_record.time = 0;
+	test_rec->rdata.tsig_record.fudge = 300;
+	test_rec->rdata.tsig_record.mac_size = UINT16_MAX;
+	test_rec->rdata.tsig_record.mac = NULL;
+	test_rec->rdata.tsig_record.original_id = UINT16_MAX;
+	test_rec->rdata.tsig_record.error = UINT16_MAX;
+	test_rec->rdata.tsig_record.other_size = 0;
+	test_rec->rdata.tsig_record.other_data = NULL;
+
+	return test_rec;
+};
+
+static struct dns_client_tkey *test_tkey_name(void) {
+	
+	struct dns_client_tkey *test_tkey = NULL;
+	test_tkey->name = "TEST_TKEY";
+
+	return test_tkey;
+};
+
+/* calls fail() if assertions are false */
+static void tkey_test(void **state)
+{
+	struct dns_client_tkey_store *test_store;
+	const char *test_name = "TEST_TKEY";
+	
+	struct dns_client_tkey *testing;
+	struct dns_client_tkey *verifier;
+
+	testing = test_tkey_name();
+	verifier  = dns_find_cli_tkey(test_store, test_name);
+
+	assert_non_null(testing);
+	assert_non_null(verifier);
+	assert_string_equal(testing->name, verifier->name);
+	
+	TALLOC_FREE(testing);
+	TALLOC_FREE(verifier);
+	return;
+}
+
+/* calls fail() if test_werr not in werr_set */
+static void gen_tsig_test(void **state)
+{
+	TALLOC_CTX *mem_ctx;
+	DATA_BLOB *in_test = {NULL, SIZE_MAX};
+	
+	struct dns_client *test_client;
+	test_client->samdb = NULL;
+	test_client->zones = NULL;
+	test_client->tkeys = NULL;
+	test_client->client_credentials = NULL;
+	test_client->max_payload = UINT16_MAX;
+	
+	struct dns_request_cli_state *test_state;
+	test_state->flags = UINT16_MAX;
+	test_state->authenticated = true;
+	test_state->sign = true;
+	test_state->key_name = "TKEY_NAME";
+	test_state->tsig->name = "TSIG_RECORD";
+	test_state->tsig->rr_type = DNS_QTYPE_TSIG;
+	test_state->tsig->rr_class = DNS_QCLASS_ANY;
+	test_state->tsig->ttl = 0;
+	test_state->tsig->length = UINT16_MAX;
+	test_state->tsig_error = UINT16_MAX;
+	
+	struct dns_name_packet *test_packet;
+	test_packet->id = UINT16_MAX;
+	test_packet->qdcount = UINT16_MAX;
+	test_packet->ancount = UINT16_MAX;
+	test_packet->nscount = UINT16_MAX;
+	test_packet->arcount = UINT16_MAX;
+
+	/* test error codes */
+	WERROR test_werr = dns_cli_generate_tsig(test_client, mem_ctx,
+								test_state, test_packet, in_test);
+
+	/* expected WERROR output */
+	assert_true(W_ERROR_IS_OK(test_werr));
+	assert_true(W_ERROR_EQUAL(WERR_NOT_ENOUGH_MEMORY, test_werr));
+	assert_true(W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), test_werr));
+	assert_true(W_ERROR_EQUAL(DNS_ERR(NOTAUTH), test_werr));
+
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* run test suite */
+int main(void)
+{
+	/* tests structure */
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(empty_sig_test),
+		cmocka_unit_test(tkey_test),
+		cmocka_unit_test(gen_tsig_test),
+	};
+
+	cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
\ No newline at end of file
-- 
2.7.4


From 2f1784cfb0df574b9d7ff9198bf752c2e71e3c83 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:22:35 +0300
Subject: [PATCH 16/23] cmocka-tests/test-fn/dns_tcp_test: individual tests for
 TCP requests feature

---
 libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c | 100 +++++++++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c

diff --git a/libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c b/libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c
new file mode 100644
index 0000000..72f1848
--- /dev/null
+++ b/libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c
@@ -0,0 +1,100 @@
+/* Tests TCP client-side DNS call handling.
+ * 
+ * Copyright 2017 (c) Dimitrios Gravanis
+ *
+ * Uses cmocka C testing API.
+ * Copyright 2013 (c) Andreas Schneider <asn at cynapses.org>
+ *                    Jakub Hrozek <jakub.hrozek at gmail.com>
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "libcli/dns/cli-fn/dns_tcp.c"
+
+
+/** test tcp send/recv functionality **/
+
+/* calls fail() if TCP test_req is NULL */
+static void test_req_send(void **state)
+{
+	TALLOC_CTX *mem_ctx;
+	struct tevent_context *test_ev;
+	const char *test_server_addr_string = "TEST_SRVR_ADDR";
+	struct iovec *test_vector;
+	size_t test_count = SIZE_MAX;
+
+	struct tevent_req *test_req = dns_tcp_req_send(mem_ctx, test_ev,
+			test_server_addr_string, test_vector, test_count);
+
+	assert_non_null(test_req);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_req_recv_reply(void **state)
+{
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_tcp_req_recv_reply(test_subreq);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_req_done(void **state)
+{
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_tcp_req_done(test_subreq);
+	return;
+}
+
+/* calls fail() if test_rcv is not 0 */
+static void test_req_recv(void **state)
+{
+	TALLOC_CTX *mem_ctx;
+	struct tevent_req *test_req;
+	uint8_t **test_reply = UINT8_MAX;
+	size_t *test_reply_len = SIZE_MAX;
+
+	int test_rcv = dns_tcp_req_recv(test_req, mem_ctx, test_reply, test_reply_len);
+	
+	assert_int_equal(test_rcv, 0);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* run test suite */
+int main(void)
+{
+	/* tests structure */
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(test_req_send),
+		cmocka_unit_test(test_req_recv_reply),
+		cmocka_unit_test(test_req_done),
+		cmocka_unit_test(test_req_recv),
+	};
+
+	cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
\ No newline at end of file
-- 
2.7.4


From 200c3b381d4b5404dfe68e17b188c4c400efc990 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:22:52 +0300
Subject: [PATCH 17/23] cmocka-tests/test-fn/dns_udp_test: individual tests for
 UDP requests feature

---
 libcli/dns/cmocka-tests/test-fn/dns_udp_test.c | 105 +++++++++++++++++++++++++
 1 file changed, 105 insertions(+)
 create mode 100644 libcli/dns/cmocka-tests/test-fn/dns_udp_test.c

diff --git a/libcli/dns/cmocka-tests/test-fn/dns_udp_test.c b/libcli/dns/cmocka-tests/test-fn/dns_udp_test.c
new file mode 100644
index 0000000..3bb98b6
--- /dev/null
+++ b/libcli/dns/cmocka-tests/test-fn/dns_udp_test.c
@@ -0,0 +1,105 @@
+/* Tests UDP client-side DNS call handling.
+ * 
+ * Copyright 2017 (c) Dimitrios Gravanis
+ *
+ * Uses cmocka C testing API.
+ * Copyright 2013 (c) Andreas Schneider <asn at cynapses.org>
+ *                    Jakub Hrozek <jakub.hrozek at gmail.com>
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "libcli/dns/cli-fn/dns_udp.c"
+
+
+/** test udp send/recv functionality **/
+
+/* calls fail() if UDP test_req is NULL */
+static void test_request_send(void **state)
+{
+	/* pending */
+	TALLOC_CTX *mem_ctx;
+	struct tevent_context *test_ev;
+	const char *test_server_addr_string = "TEST_SRVR_ADDR";
+	const uint8_t *test_query = UINT8_MAX;
+	size_t test_query_len = SIZE_MAX;
+
+	struct tevent_req *test_req = dns_udp_request_send(mem_ctx, test_ev,
+			test_server_addr_string, test_query, test_query_len);
+	
+	assert_non_null(test_req);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_request_get_reply(void **state)
+{
+	/* pending */
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_udp_request_get_reply(test_subreq);
+	return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_request_done(void **state)
+{
+	/* pending */
+	struct tevent_req *test_subreq;
+	assert_non_null(test_subreq);
+	dns_udp_request_done(test_subreq);
+	return;
+}
+
+/* calls fail() if test_rcv is not 0 */
+static void test_request_recv(void **state)
+{
+	/* incomplete */
+	struct tevent_req *test_req;
+	TALLOC_CTX *mem_ctx;
+	uint8_t **test_reply = UINT8_MAX;
+	size_t *test_reply_len = SIZE_MAX;
+
+	/* pending */
+	int test_rcv = dns_udp_request_recv(test_req, mem_ctx, test_reply, test_reply_len);
+	
+	assert_int_equal(test_rcv, 0);
+	TALLOC_FREE(mem_ctx);
+	return;
+}
+
+/* run test suite */
+int main(void)
+{
+	/* tests structure */
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(test_request_send),
+		cmocka_unit_test(test_request_get_reply),
+		cmocka_unit_test(test_request_done),
+		cmocka_unit_test(test_request_recv),
+	};
+
+	cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
\ No newline at end of file
-- 
2.7.4


From 15fa0f5c0af2c9b69063a4d07fb165c8568ff921 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:23:38 +0300
Subject: [PATCH 18/23] cmocka-tests/test-fn/wscript: waf script to build
 individual tests *incomplete*

---
 libcli/dns/cmocka-tests/test-fn/wscript | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100755 libcli/dns/cmocka-tests/test-fn/wscript

diff --git a/libcli/dns/cmocka-tests/test-fn/wscript b/libcli/dns/cmocka-tests/test-fn/wscript
new file mode 100755
index 0000000..2afb70c
--- /dev/null
+++ b/libcli/dns/cmocka-tests/test-fn/wscript
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+import os
+
+out = 'build-tests'
+
+# configures and builds individual tests in /cmocka-tests/test-fn
+def configure(conf):
+	print('configuring test suites in ' + out + '...')
+	return
+
+def build(bld):
+	print('building cli_crypto_test...')
+	bld.SAMBA_BINARY('cli_crypto_test',
+			source='cli_crypto_test.c',
+			deps='LIBTSOCKET tevent-util cmocka',
+			install=False)
+
+	print('building dns_tcp_test...')
+	bld.SAMBA_BINARY('dns_tcp_test',
+			source='dns_tcp_test.c',
+			deps='LIBTSOCKET tevent-util cmocka',
+			install=False)
+
+	print('building dns_udp_test...')
+	bld.SAMBA_BINARY('dns_udp_test',
+			source='dns_udp_test.c',
+			deps='LIBTSOCKET tevent-util cmocka',
+			install=False)
+			
+	print('DONE')
-- 
2.7.4


From e9dd5fac73d4a2e4ddeda54fc8c71f87a8ef9ce0 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:24:38 +0300
Subject: [PATCH 19/23] source4/dns_server/dns_query: replaced libdns.h with
 libudp.h in preprocessor

---
 source4/dns_server/dns_query.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c
index fa92721..e72a9ef 100644
--- a/source4/dns_server/dns_query.c
+++ b/source4/dns_server/dns_query.c
@@ -30,7 +30,7 @@
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/common/util.h"
 #include "dns_server/dns_server.h"
-#include "libcli/dns/libdns.h"
+#include "libcli/dns/libudp.h"
 #include "lib/util/dlinklist.h"
 #include "lib/util/util_net.h"
 #include "lib/util/tevent_werror.h"
-- 
2.7.4


From d465463eb33bd0accff80866f6300cab7b70c720 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:25:35 +0300
Subject: [PATCH 20/23] wscript_build: added recursive building for
 libcli/dns/cmocka-tests

---
 wscript_build | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/wscript_build b/wscript_build
index 8758b6d..43f89a3 100644
--- a/wscript_build
+++ b/wscript_build
@@ -120,12 +120,14 @@ bld.RECURSE('libcli/lsarpc')
 bld.RECURSE('libcli/drsuapi')
 bld.RECURSE('libcli/echo')
 bld.RECURSE('libcli/dns')
+bld.RECURSE('libcli/dns/cmocka-tests')
 bld.RECURSE('libcli/samsync')
 bld.RECURSE('libcli/registry')
 bld.RECURSE('source4/lib/policy')
 bld.RECURSE('libcli/named_pipe_auth')
 if bld.CONFIG_GET('ENABLE_SELFTEST'):
     bld.RECURSE('testsuite/unittests')
+    bld.RECURSE('libcli/dns/cmocka-tests/test-fn')
 
 if bld.CONFIG_GET('KRB5_VENDOR') in (None, 'heimdal'):
     if bld.CONFIG_GET("HEIMDAL_KRB5_CONFIG") and bld.CONFIG_GET("USING_SYSTEM_KRB5"):
-- 
2.7.4


From 193386d977e013e2e187512c7e48b6460bdd5dd1 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:26:08 +0300
Subject: [PATCH 21/23] libcli/dns/libdns: renamed to libudp

---
 libcli/dns/libdns.h | 53 -----------------------------------------------------
 1 file changed, 53 deletions(-)
 delete mode 100644 libcli/dns/libdns.h

diff --git a/libcli/dns/libdns.h b/libcli/dns/libdns.h
deleted file mode 100644
index 7ea2eb6..0000000
--- a/libcli/dns/libdns.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-
-   Small async DNS library for Samba with socketwrapper support
-
-   Copyright (C) 2012 Kai Blin  <kai at 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__
-
-/** Send an dns request to a dns server using UDP
- *
- *@param mem_ctx        talloc memory context to use
- *@param ev             tevent context to use
- *@param server_address address of the server as a string
- *@param query          dns query to send
- *@param query_len      length of the query
- *@return tevent_req with the active request or NULL on out-of-memory
- */
-struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
-					struct tevent_context *ev,
-					const char *server_address,
-					const uint8_t *query,
-					size_t query_len);
-
-/** Get the dns response from a dns server via UDP
- *
- *@param req       tevent_req struct returned from dns_request_send
- *@param mem_ctx   talloc memory context to use for the reply string
- *@param reply     buffer that will be allocated and filled with the dns reply
- *@param reply_len length of the reply buffer
- *@return 0/errno
- */
-int dns_udp_request_recv(struct tevent_req *req,
-			 TALLOC_CTX *mem_ctx,
-			 uint8_t **reply,
-			 size_t *reply_len);
-
-#endif /*__LIBDNS_H__*/
-- 
2.7.4


From ed46adf41b04b7b1eb4e9809e0f2fc0a9c11c610 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:27:00 +0300
Subject: [PATCH 22/23] libcli/dns/wscript_build: modified to build clidns
 subsystem with cli_dns

---
 libcli/dns/wscript_build | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libcli/dns/wscript_build b/libcli/dns/wscript_build
index 018df6b..6923f7f 100644
--- a/libcli/dns/wscript_build
+++ b/libcli/dns/wscript_build
@@ -1,5 +1,7 @@
 #!/usr/bin/env python
 
+# builds a library for DNS TCP/UDP calls that utilizes GSS-TSIG encryption
 bld.SAMBA_SUBSYSTEM('clidns',
-        source='dns.c',
-        public_deps='LIBTSOCKET tevent-util')
+	    source='cli_dns.c',
+	    public_deps='LIBTSOCKET tevent-util',
+	    deps='gensec auth samba_server_gensec dnsserver_common')
-- 
2.7.4


From c8a878c321fae7fcbb6c195df2ae5794c8b4856d Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav at gmail.com>
Date: Sun, 27 Aug 2017 14:27:34 +0300
Subject: [PATCH 23/23] libcli/dns/dns.c: replaced by cli_dns

---
 libcli/dns/dns.c | 178 -------------------------------------------------------
 1 file changed, 178 deletions(-)
 delete mode 100644 libcli/dns/dns.c

diff --git a/libcli/dns/dns.c b/libcli/dns/dns.c
deleted file mode 100644
index 7d066d8..0000000
--- a/libcli/dns/dns.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-
-   Small async DNS library for Samba with socketwrapper support
-
-   Copyright (C) 2010 Kai Blin  <kai at 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 "libcli/util/error.h"
-#include "librpc/gen_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 2
-
-/* 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);
-
-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_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))) {
-		tevent_req_oom(req);
-		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);
-}
-
-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;
-}
-- 
2.7.4



More information about the samba-technical mailing list