From 25b777fa9d60463685a4dbba6fd2e0413b846caf Mon Sep 17 00:00:00 2001 From: cel Date: Fri, 30 Nov 2018 21:55:42 -1000 Subject: [PATCH] Add -a passthrough option --- README.md | 6 ++-- sbotc.1 | 8 +++++ sbotc.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8c8f91a..8e59016 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,10 @@ sudo make install ## Usage ```sh -sbotc [-j] [-T] [-n] [-c ] [-s ] [-p ] [-u ] [-k ] [-K ] - [-t ] [...] +sbotc [-j] [-T] + [ -n | [-c ] [-k ] [-K ] ] + [ [-s ] [-p ] [ -4 | -6 ] | [-u ] ] + [ -a | [-t ] [...] ] ``` Arguments must be explicitly JSON-encoded. diff --git a/sbotc.1 b/sbotc.1 index d91fb77..ee65468 100644 --- a/sbotc.1 +++ b/sbotc.1 @@ -9,6 +9,7 @@ .Nm .Op Fl j .Op Fl T +.Op Fl a . .Oo .Fl n @@ -30,9 +31,13 @@ .Op Fl u Ar socket_path .Oc . +.Oo +.Fl a +| .Op Fl t Ar type .Ar method .Op Ar argument ... +.Oc .Sh DESCRIPTION Connect to a scuttlebot/secret-stack server, and call a method on it, with standard I/O. @@ -44,6 +49,9 @@ Send stdin data as JSON. Test using shs1-testsuite protocol. Instead of connecting to a server and running a command, connect to stdio. On successful handshake, output concatenation of the encryption key, encryption nonce, decryption key and decryption nonce. +.It Fl a +Passthrough mode. Instead of making a muxrpc call, pass through the box-stream +to stdio. .It Fl n Noauth mode. Skip secret-handshake authentication and box-stream encryption. This option makes the diff --git a/sbotc.c b/sbotc.c index 23e28c8..c26eaf1 100644 --- a/sbotc.c +++ b/sbotc.c @@ -106,7 +106,7 @@ static void usage() { fputs("usage: sbotc [-j] [-T]\n" " [ -n | [-c ] [-k ] [-K ] ]\n" " [ [-s ] [-p ] [ -4 | -6 ] | [-u ] ]\n" - " [-t ] [...]\n", stderr); + " [ -a | [-t ] [...] ]\n", stderr); exit(EXIT_FAILURE); } @@ -176,6 +176,16 @@ static int read_all(int fd, void *buf, size_t count) { return 0; } +static int read_some(int fd, unsigned char *buf, size_t *lenp) { + ssize_t nbytes; + do nbytes = read(fd, buf, *lenp); + while (nbytes < 0 && errno == EINTR); + if (nbytes == 0) { errno = EPIPE; return -1; } + if (nbytes < 0) return -1; + *lenp = nbytes; + return 0; +} + static int write_all(int fd, const void *buf, size_t count) { ssize_t nbytes; while (count > 0) { @@ -485,12 +495,20 @@ static void bs_end(struct boxs *bs) { if (!bs->noauth) { bs_write_end_box(bs); } + shutdown(bs->s, SHUT_WR); } static int bs_read_packet(struct boxs *bs, void *buf, size_t *lenp) { + int rc; + if (bs->noauth) { + rc = read_some(bs->s, buf, lenp); + if (rc < 0 && errno == EPIPE) return -1; + if (rc < 0) err(1, "failed to read packet data"); + return 0; + } unsigned char boxed_header[34]; struct boxs_header header; - int rc = read_all(bs->s, boxed_header, 34); + rc = read_all(bs->s, boxed_header, 34); if (rc < 0 && errno == EPIPE) errx(1, "unexpected end of parent stream"); if (rc < 0) err(1, "failed to read boxed packet header"); rc = crypto_secretbox_open_easy((unsigned char *)&header, boxed_header, 34, bs->rx_nonce, bs->decrypt_key); @@ -531,6 +549,18 @@ static int bs_read(struct boxs *bs, char *buf, size_t len) { return 0; } +static enum stream_state bs_read_out_1(struct boxs *bs, int fd) { + size_t buf[4096]; + size_t len = sizeof(buf); + int rc; + rc = bs_read_packet(bs, buf, &len); + if (rc < 0 && errno == EPIPE) return stream_state_ended_ok; + if (rc < 0) return stream_state_ended_error; + rc = write_all(fd, buf, len); + if (rc < 0) return stream_state_ended_error; + return stream_state_open; +} + static int bs_read_out(struct boxs *bs, int fd, size_t len) { size_t chunk; char buf[4096]; @@ -578,6 +608,18 @@ static void bs_write(struct boxs *bs, const unsigned char *buf, size_t len) { } } +static enum stream_state bs_write_in_1(struct boxs *bs, int fd) { + unsigned char buf[4096]; + ssize_t sz = read(fd, buf, sizeof(buf)); + if (sz < 0) err(1, "read"); + if (sz == 0) { + bs_end(bs); + return stream_state_ended_ok; + } + bs_write(bs, buf, sz); + return stream_state_open; +} + static void ps_write(struct boxs *bs, const char *data, size_t len, enum pkt_type type, int req_id, bool stream, bool end) { size_t out_len = 9 + len; unsigned char out_buf[out_len]; @@ -619,6 +661,29 @@ static void muxrpc_call(struct boxs *bs, const char *method, const char *argumen ps_write(bs, req, reqlen, pkt_type_json, req_id, !is_request, false); } +static int bs_passthrough(struct boxs *bs, int infd, int outfd) { + int rc; + fd_set rd; + int sfd = bs->s; + int maxfd = infd > sfd ? infd : sfd; + enum stream_state in = stream_state_open; + enum stream_state out = stream_state_open; + + while (out == stream_state_open + || (in == stream_state_open && out != stream_state_ended_error)) { + FD_ZERO(&rd); + if (in == stream_state_open) FD_SET(infd, &rd); + if (out == stream_state_open) FD_SET(sfd, &rd); + rc = select(maxfd + 1, &rd, 0, 0, NULL); + if (rc < 0) err(1, "select"); + if (FD_ISSET(infd, &rd)) in = bs_write_in_1(bs, infd); + if (FD_ISSET(sfd, &rd)) out = bs_read_out_1(bs, outfd); + } + + return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 : + in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1; +} + static void ps_reject(struct boxs *bs, size_t len, int32_t req, enum pkt_flags flags) { // ignore the packet. if this is a request, the substream on the other end // will just have to wait until the rpc connection closes. @@ -866,7 +931,7 @@ int main(int argc, char *argv[]) { const char *keypair_seed_str = NULL; const char *host = NULL; const char *port = "8008"; - const char *typestr = NULL, *methodstr; + const char *typestr = NULL, *methodstr = NULL; const char *shs_cap_key_str = NULL; const char *socket_path = NULL; size_t argument_len; @@ -887,6 +952,7 @@ int main(int argc, char *argv[]) { bool shs_cap_key_str_arg = false; bool ipv4_arg = false; bool ipv6_arg = false; + bool passthrough = false; enum ip_family ip_family; get_app_dir(app_dir, sizeof(app_dir)); @@ -920,10 +986,12 @@ int main(int argc, char *argv[]) { case 'n': noauth = true; break; case '4': ipv4_arg = true; break; case '6': ipv6_arg = true; break; + case 'a': passthrough = true; break; default: usage(); } } - if (i < argc) methodstr = argv[i++]; else if (!test) usage(); + if (i < argc) methodstr = argv[i++]; + else if (!test && !passthrough) usage(); if (ipv4_arg && ipv6_arg) errx(1, "options -4 and -6 conflict"); ip_family = @@ -941,9 +1009,15 @@ int main(int argc, char *argv[]) { argument_len = test ? 0 : args_to_json_length(argc-i, argv+i); char argument[argument_len]; - if (!test) { + if (passthrough) { + if (methodstr) errx(1, "-a option conflicts with method"); + if (typestr) errx(1, "-a option conflicts with -t option"); + if (argc-i > 0) errx(1, "-a option conflicts with method arguments"); + if (test) errx(1, "-a option conflicts with -T test"); + + } else if (!test) { rc = args_to_json(argument, sizeof(argument), argc-i, argv+i); - if (rc < 0) errx(0, "unable to collect arguments"); + if (rc < 0) errx(1, "unable to collect arguments"); char manifest_buf[8192]; if (!typestr) { @@ -1040,6 +1114,12 @@ do_tcp_connect: return 0; } + if (passthrough) { + rc = bs_passthrough(&bs, STDIN_FILENO, STDOUT_FILENO); + close(s); + return rc; + } + muxrpc_call(&bs, method, argument, type, typestr, 1); switch (type) {