diff options
Diffstat (limited to 'src/proone-htbtclient.c')
-rw-r--r-- | src/proone-htbtclient.c | 339 |
1 files changed, 319 insertions, 20 deletions
diff --git a/src/proone-htbtclient.c b/src/proone-htbtclient.c index c7328f1..783a797 100644 --- a/src/proone-htbtclient.c +++ b/src/proone-htbtclient.c @@ -116,6 +116,20 @@ "Note that an instance will continue to run with original binary if it fails to\n"\ "exec() to the new binary.\n"\ "\n" +#define RCB_HELP_STR \ +"Order instance to do binary recombination and download the binary.\n"\ +"Usage: %s [common options] rcb [options] [OUTFILE]\n"\ +"\n"\ +"Options:\n"\ +" --arch <ARCH> target CPU ARCH\n"\ +" --no-compat do not allow recombination of compatible arch\n"\ +" -f overwrite OUTFILE\n"\ +"\n"\ +"The program will write to stdout if OUTFILE is \"-\"(default).\n"\ +"If --arch option is not used, binary recombination will not take place and the\n"\ +"running executable will be copied(i.e., \"self copy\").\n"\ +"Run proone-list-arch for possible values for --arch option.\n"\ +"\n" enum sub_command { SC_NONE, @@ -163,6 +177,11 @@ struct { bool detached; bool compat; } run; + struct { + char *out_path; + bool f; + prne_htbt_rcb_t rcb; + } rcb; } cmd_param; void (*free_cmdparam_f)(void); } prog_conf; @@ -183,6 +202,8 @@ struct { } net; struct { yaml_emitter_t *emitter; + int fd; + bool our_fd; } yaml; union { struct { @@ -193,6 +214,11 @@ struct { prne_iobuf_t ib; prne_htbt_status_t st; } run; + struct { + int fd; + bool our_file; + prne_iobuf_t ib; + } rcb; } cmd_st; void (*free_cmdst_f)(void); } prog_g; @@ -214,7 +240,9 @@ static void print_help (const char *prog, const sub_command_t sc, FILE *out_f) { case SC_UPBIN: fprintf(out_f, UPBIN_HELP_STR, prog); break; - // TODO + case SC_RCB: + fprintf(out_f, RCB_HELP_STR, prog); + break; default: fprintf(out_f, MAIN_HELP_STR, prog, prog); } } @@ -239,10 +267,13 @@ static void init_prog_g (void) { perror("prne_alloc_iobuf()"); abort(); } + + prog_g.yaml.fd = -1; } static void free_run_g (void) { prne_close(prog_g.cmd_st.run.fd); + prog_g.cmd_st.run.fd = -1; prne_free_iobuf(&prog_g.cmd_st.run.ib); prne_htbt_free_status(&prog_g.cmd_st.run.st); } @@ -259,6 +290,24 @@ static void init_run_g (void) { prog_g.free_cmdst_f = free_run_g; } +static void free_rcb_g (void) { + prne_close(prog_g.cmd_st.rcb.fd); + prog_g.cmd_st.rcb.fd = -1; + prne_free_iobuf(&prog_g.cmd_st.rcb.ib); +} + +static void init_rcb_g (void) { + assert(prog_g.free_cmdst_f == NULL); + + prog_g.cmd_st.rcb.fd = -1; + prne_init_iobuf(&prog_g.cmd_st.rcb.ib); + if (!prne_alloc_iobuf(&prog_g.cmd_st.rcb.ib, PRNE_HTBT_STDIO_LEN_MAX)) { + perror("prne_alloc_iobuf()"); + abort(); + } + prog_g.free_cmdst_f = free_rcb_g; +} + static void deinit_prog_g (void) { // TODO if (prog_g.free_cmdst_f != NULL) { @@ -289,14 +338,32 @@ 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); + prog_conf.cmd_param.run.bin_path = NULL; } static void init_run_conf (void) { + assert(prog_conf.free_cmdparam_f == NULL); 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; } +static void free_rcb_conf (void) { + prne_free(prog_conf.cmd_param.rcb.out_path); + prog_conf.cmd_param.rcb.out_path = NULL; + prne_htbt_free_rcb(&prog_conf.cmd_param.rcb.rcb); +} + +static void init_rcb_conf (void) { + assert(prog_conf.free_cmdparam_f == NULL); + prne_htbt_init_rcb(&prog_conf.cmd_param.rcb.rcb); + prog_conf.cmd_param.rcb.out_path = prne_dup_str("-"); + prog_conf.cmd_param.rcb.rcb.compat = true; + + prog_conf.free_cmdparam_f = free_rcb_conf; +} + static void deinit_prog_conf (void) { if (prog_conf.free_cmdparam_f != NULL) { prog_conf.free_cmdparam_f(); @@ -586,10 +653,56 @@ static int parse_args_upbin (const int argc, char *const *args) { } static int parse_args_rcb (const int argc, char *const *args) { + static const struct option lopts[] = { + { "arch", required_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; } - // TODO + init_rcb_conf(); + init_rcb_g(); + + while (true) { + f_ret = getopt_long(argc, args, "f", lopts, &li); + if (f_ret == 'f') { + prog_conf.cmd_param.rcb.f = true; + continue; + } + else if (f_ret != 0) { + break; + } + + co = (const struct option*)lopts + li; + if (strcmp("arch", co->name) == 0) { + prog_conf.cmd_param.rcb.rcb.arch = prne_arch_fstr(optarg); + if (prog_conf.cmd_param.rcb.rcb.arch == PRNE_ARCH_NONE) { + perror(optarg); + return 2; + } + } + else if (strcmp("no-compat", co->name) == 0) { + prog_conf.cmd_param.rcb.rcb.compat = false; + } + else { + abort(); + } + } + + if (argc > optind) { + prne_free(prog_conf.cmd_param.rcb.out_path); + prog_conf.cmd_param.rcb.out_path = prne_dup_str(args[optind]); + if (prog_conf.cmd_param.rcb.out_path == NULL) { + perror("prne_dup_str()"); + abort(); + } + optind += 1; + } + return 0; } @@ -914,7 +1027,7 @@ static int yaml_output_handler(void *data, unsigned char *buffer, size_t size) { ssize_t io_ret; while (size > 0) { - io_ret = write(STDOUT_FILENO, buffer, size); + io_ret = write(prog_g.yaml.fd, buffer, size); if (io_ret <= 0) { if (io_ret < 0) { perror("write()"); @@ -1004,6 +1117,10 @@ static void emit_scalar (const char *type, const char *val) { } } +static void emit_bool_scalar (const bool val) { + emit_scalar(YAML_BOOL_TAG, val ? "true" : "false"); +} + static void emit_scalar_fmt (const char *type, const char *fmt, ...) { char *str; int f_ret; @@ -1026,14 +1143,13 @@ static void emit_scalar_fmt (const char *type, const char *fmt, ...) { prne_free(str); } -static void start_yaml (void) { +static void start_yaml (const int fd, const bool ours) { yaml_event_t e; - if (prog_g.yaml.emitter != NULL) { - fprintf(stderr, "start_yaml() called twice!\n"); - abort(); - } + assert(prog_g.yaml.emitter == NULL); + prog_g.yaml.fd = fd; + prog_g.yaml.our_fd = ours; prog_g.yaml.emitter = prne_malloc(sizeof(yaml_emitter_t), 1); if (yaml_emitter_initialize(prog_g.yaml.emitter) == 0) { yaml_perror("yaml_emitter_initialize()"); @@ -1079,6 +1195,13 @@ static void end_yaml (void) { yaml_perror("yaml_stream_end_event_initialize()"); abort(); } + + yaml_emitter_delete(prog_g.yaml.emitter); + prog_g.yaml.emitter = NULL; + if (prog_g.yaml.our_fd) { + prne_close(prog_g.yaml.fd); + prog_g.yaml.fd = -1; + } } static bool do_connect (void) { @@ -1163,6 +1286,17 @@ static void raise_proto_err (const char *fmt, ...) { fprintf(stderr, "\n"); } +static void raise_invalid_op (const prne_htbt_op_t op) { + raise_proto_err("invalid response op %"PRIx8"", op); +} + +static void raise_invalid_status (const prne_htbt_status_t *st) { + raise_proto_err( + "Invalid status response: code=%"PRIx8", err=%"PRId32, + st->code, + st->err); +} + static bool send_frame (const void *frame, prne_htbt_ser_ft ser_f) { int f_ret; size_t actual; @@ -1300,6 +1434,10 @@ static void emit_status_frame (const prne_htbt_status_t *st) { emit_scalar_fmt(YAML_INT_TAG, "%d", st->code); emit_scalar(YAML_STR_TAG, "err"); emit_scalar_fmt(YAML_INT_TAG, "%"PRId32, st->err); + if (st->code == PRNE_HTBT_STATUS_ERRNO) { + emit_scalar(YAML_STR_TAG, "err_str"); + emit_scalar(YAML_STR_TAG, strerror(st->err)); + } emit_mapping_end(); } @@ -1545,7 +1683,7 @@ static bool do_hostinfo ( *status = true; break; default: - raise_proto_err("invalid response op %"PRIx8"\n", mh.op); + raise_invalid_op(mh.op); goto END; } ret = true; @@ -1573,13 +1711,12 @@ static int cmdmain_hostinfo (void) { goto END; } + start_yaml(STDOUT_FILENO, false); 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); } @@ -1860,7 +1997,7 @@ static bool run_recvstd (const uint16_t msgid, int *fd) { break; default: ret = false; - raise_proto_err("invalid response op %"PRIx8"\n", mh.op); + raise_invalid_op(mh.op); goto END; } @@ -1968,10 +2105,7 @@ static int cmdmain_run (void) { perror("Error status"); return 1; default: - raise_proto_err( - "Invalid response: code=%"PRIx8", err=%"PRId32, - prog_g.cmd_st.run.st.code, - prog_g.cmd_st.run.st.err); + raise_invalid_status(&prog_g.cmd_st.run.st); return 1; } } while (false); @@ -1986,6 +2120,8 @@ static void emit_upbin_opts (void) { 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, "compat"); + emit_bool_scalar(prog_conf.cmd_param.run.compat); 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"); @@ -2229,7 +2365,7 @@ static int cmdmain_upbin (void) { return 1; } - start_yaml(); + start_yaml(STDOUT_FILENO, false); emit_preemble("upbin", "ok", emit_upbin_opts); emit_status_frame(&prog_g.cmd_st.run.st); @@ -2267,7 +2403,7 @@ static int cmdmain_hover (void) { goto END; } if (mh.op != PRNE_HTBT_OP_STATUS) { - raise_proto_err("invalid response op %"PRIx8"\n", mh.op); + raise_invalid_op(mh.op); ret = 1; goto END; } @@ -2276,7 +2412,7 @@ static int cmdmain_hover (void) { ret = 1; goto END; } - start_yaml(); + start_yaml(STDOUT_FILENO, false); emit_preemble("hover", "ok", emit_hover_opts); emit_status_frame(&st); @@ -2286,6 +2422,169 @@ END: return ret; } +static void emit_rcb_opts (void) { + emit_scalar(YAML_STR_TAG, PREEMBLE_OPT_TAG_NAME); + emit_mapping_start(); + if (prog_conf.cmd_param.rcb.rcb.arch != PRNE_ARCH_NONE) { + emit_scalar(YAML_STR_TAG, "arch"); + emit_scalar( + YAML_STR_TAG, + prne_arch_tostr(prog_conf.cmd_param.rcb.rcb.arch)); + } + emit_scalar(YAML_STR_TAG, "compat"); + emit_bool_scalar(prog_conf.cmd_param.rcb.rcb.compat); + emit_mapping_end(); +} + +static bool rcb_open_outfile (void) { + assert(prog_g.cmd_st.rcb.fd < 0); + assert(prog_conf.cmd_param.rcb.out_path != NULL); + if (strcmp(prog_conf.cmd_param.rcb.out_path, "-") == 0) { + prog_g.cmd_st.rcb.fd = STDOUT_FILENO; + prog_g.cmd_st.rcb.our_file = false; + } + else { + if (!prog_conf.cmd_param.rcb.f) { + if (access(prog_conf.cmd_param.rcb.out_path, F_OK) == 0) { + errno = EEXIST; + perror(prog_conf.cmd_param.rcb.out_path); + return false; + } + } + + prog_g.cmd_st.rcb.fd = open( + prog_conf.cmd_param.rcb.out_path, + O_CREAT | O_TRUNC | O_WRONLY, + 0755); + if (prog_g.cmd_st.rcb.fd < 0) { + perror(prog_conf.cmd_param.rcb.out_path); + return false; + } + prog_g.cmd_st.rcb.our_file = true; + } + + return true; +} + +static int cmdmain_rcb (void) { + int ret = 0; + prne_htbt_msg_head_t mh; + prne_htbt_status_t st; + prne_htbt_stdio_t df; + uint16_t msgid = prne_htbt_gen_msgid(NULL, htbt_msgid_rnd_f); + ssize_t io_ret; + size_t sum = 0; + + prne_htbt_init_msg_head(&mh); + prne_htbt_init_status(&st); + prne_htbt_init_stdio(&df); + + if (!rcb_open_outfile()) { + ret = 1; + goto END; + } + if (isatty(prog_g.cmd_st.rcb.fd)) { + ret = 1; + fprintf(stderr, "Cannot write binary data to terminal.\n"); + goto END; + } + if (!do_connect()) { + ret = 1; + goto END; + } + mh.id = msgid; + mh.op = PRNE_HTBT_OP_RCB; + if (!(send_mh(&mh) && + send_frame( + &prog_conf.cmd_param.rcb.rcb, + (prne_htbt_ser_ft)prne_htbt_ser_rcb))) + { + ret = 1; + goto END; + } + + do { + if (!recv_mh(&mh, &msgid)) { + ret = 1; + goto END; + } + switch (mh.op) { + case PRNE_HTBT_OP_STDIO: break; + case PRNE_HTBT_OP_STATUS: + if (!recv_status(&st)) { + ret = 1; + goto END; + } + start_yaml(STDERR_FILENO, false); + emit_preemble("rcb", "status", emit_rcb_opts); + emit_status_frame(&st); + goto END; + default: + ret = 1; + raise_invalid_op(mh.op); + goto END; + } + + if (!recv_frame(&df, (prne_htbt_dser_ft)prne_htbt_dser_stdio)) { + ret = 1; + goto END; + } + while (df.len > 0) { + io_ret = mbedtls_ssl_read( + &prog_g.ssl.ctx, + prog_g.cmd_st.rcb.ib.m, + df.len); + if (io_ret == 0) { + ret = 1; + raise_proto_err("remote end shutdown write"); + goto END; + } + if (io_ret < 0) { + ret = 1; + prne_mbedtls_perror(io_ret, "mbedtls_ssl_read()"); + goto END; + } + prne_iobuf_shift(&prog_g.cmd_st.rcb.ib, io_ret); + df.len -= io_ret; + sum += io_ret; + + while (prog_g.cmd_st.rcb.ib.len > 0) { + io_ret = write( + prog_g.cmd_st.rcb.fd, + prog_g.cmd_st.rcb.ib.m, + prog_g.cmd_st.rcb.ib.len); + if (io_ret < 0) { + ret = 1; + perror("write()"); + goto END; + } + if (io_ret == 0) { + ret = 1; + fprintf(stderr, "write() EOF\n"); + goto END; + } + prne_iobuf_shift(&prog_g.cmd_st.rcb.ib, -io_ret); + } + } + } while (!df.fin); + start_yaml(STDERR_FILENO, false); + emit_preemble("rcb", "ok", emit_rcb_opts); + emit_scalar(YAML_STR_TAG, BODY_TAG_NAME); + emit_mapping_start(); + emit_scalar(YAML_STR_TAG, "size"); + emit_scalar_fmt(YAML_INT_TAG, "%zu", sum); + emit_mapping_end(); + +END: + prne_htbt_free_msg_head(&mh); + prne_htbt_free_status(&st); + prne_htbt_free_stdio(&df); + if (ret != 0 && prog_g.cmd_st.rcb.our_file) { + unlink(prog_conf.cmd_param.rcb.out_path); + } + return ret; +} + int main (const int argc, char *const *args) { int ec = 0; @@ -2320,7 +2619,7 @@ int main (const int argc, char *const *args) { case SC_RUNCMD: case SC_RUNBIN: ec = cmdmain_run(); break; case SC_UPBIN: ec = cmdmain_upbin(); break; - // TODO + case SC_RCB: ec = cmdmain_rcb(); break; default: ec = 1; fprintf(stderr, "COMMAND not implemented.\n"); |