X-Git-Url: https://www.bearssl.org/gitweb//home/git/?p=BearSSL;a=blobdiff_plain;f=tools%2Fsslio.c;h=99410900f729c7003b5c3dba3b33c25e32ba14f8;hp=14a3138825d3985f5645027c9309d758c60dfb45;hb=3f00688b9d9f483a6ca97e659eea104995ea15b7;hpb=3655193439d4e093bb80a2f0d9e02179d424de08 diff --git a/tools/sslio.c b/tools/sslio.c index 14a3138..9941090 100644 --- a/tools/sslio.c +++ b/tools/sslio.c @@ -28,6 +28,10 @@ #include #include +#ifdef _WIN32 +#include +#include +#else #include #include #include @@ -37,8 +41,11 @@ #include #include +#define SOCKET int +#define INVALID_SOCKET (-1) +#endif + #include "brssl.h" -#include "bearssl.h" static void dump_blob(const char *name, const void *data, size_t len) @@ -134,25 +141,173 @@ run_command(br_ssl_engine_context *cc, unsigned char *buf, size_t len) } } +#ifdef _WIN32 + +typedef struct { + unsigned char buf[1024]; + size_t ptr, len; +} in_buffer; + +static int +in_return_bytes(in_buffer *bb, unsigned char *buf, size_t len) +{ + if (bb->ptr < bb->len) { + size_t clen; + + if (buf == NULL) { + return 1; + } + clen = bb->len - bb->ptr; + if (clen > len) { + clen = len; + } + memcpy(buf, bb->buf + bb->ptr, clen); + bb->ptr += clen; + if (bb->ptr == bb->len) { + bb->ptr = bb->len = 0; + } + return (int)clen; + } + return 0; +} + +/* + * A buffered version of in_read(), using a buffer to return only + * full lines when feasible. + */ +static int +in_read_buffered(HANDLE h_in, in_buffer *bb, unsigned char *buf, size_t len) +{ + int n; + + if (len == 0) { + return 0; + } + n = in_return_bytes(bb, buf, len); + if (n != 0) { + return n; + } + for (;;) { + INPUT_RECORD inrec; + DWORD v; + + if (!PeekConsoleInput(h_in, &inrec, 1, &v)) { + fprintf(stderr, "ERROR: PeekConsoleInput()" + " failed with 0x%08lX\n", + (unsigned long)GetLastError()); + return -1; + } + if (v == 0) { + return 0; + } + if (!ReadConsoleInput(h_in, &inrec, 1, &v)) { + fprintf(stderr, "ERROR: ReadConsoleInput()" + " failed with 0x%08lX\n", + (unsigned long)GetLastError()); + return -1; + } + if (v == 0) { + return 0; + } + if (inrec.EventType == KEY_EVENT + && inrec.Event.KeyEvent.bKeyDown) + { + int c; + + c = inrec.Event.KeyEvent.uChar.AsciiChar; + if (c == '\n' || c == '\r' || c == '\t' + || (c >= 32 && c != 127)) + { + if (c == '\r') { + c = '\n'; + } + bb->buf[bb->ptr ++] = (unsigned char)c; + printf("%c", c); + fflush(stdout); + bb->len = bb->ptr; + if (bb->len == sizeof bb->buf || c == '\n') { + bb->ptr = 0; + return in_return_bytes(bb, buf, len); + } + } + } + } +} + +static int +in_avail_buffered(HANDLE h_in, in_buffer *bb) +{ + return in_read_buffered(h_in, bb, NULL, 1); +} + +#endif + /* see brssl.h */ int -run_ssl_engine(br_ssl_engine_context *cc, int fd, unsigned flags) +run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) { int hsdetails; int retcode; int verbose; int trace; +#ifdef _WIN32 + WSAEVENT fd_event; + int can_send, can_recv; + HANDLE h_in, h_out; + in_buffer bb; +#endif hsdetails = 0; retcode = 0; verbose = (flags & RUN_ENGINE_VERBOSE) != 0; trace = (flags & RUN_ENGINE_TRACE) != 0; +#ifdef _WIN32 + fd_event = WSA_INVALID_EVENT; + can_send = 0; + can_recv = 0; + bb.ptr = bb.len = 0; +#endif + + /* + * On Unix systems, we need to follow three descriptors: + * standard input (0), standard output (1), and the socket + * itself (for both read and write). This is done with a poll() + * call. + * + * On Windows systems, we use WSAEventSelect() to associate + * an event handle with the network activity, and we use + * WaitForMultipleObjectsEx() on that handle and the standard + * input handle, when appropriate. Standard output is assumed + * to be always writeable, and standard input to be the console; + * this does not work well (or at all) with redirections (to + * pipes or files) but it should be enough for a debug tool + * (TODO: make something that handles redirections as well). + */ + +#ifdef _WIN32 + fd_event = WSACreateEvent(); + if (fd_event == WSA_INVALID_EVENT) { + fprintf(stderr, "ERROR: WSACreateEvent() failed with %d\n", + WSAGetLastError()); + retcode = -2; + goto engine_exit; + } + WSAEventSelect(fd, fd_event, FD_READ | FD_WRITE | FD_CLOSE); + h_in = GetStdHandle(STD_INPUT_HANDLE); + h_out = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleMode(h_in, ENABLE_ECHO_INPUT + | ENABLE_LINE_INPUT + | ENABLE_PROCESSED_INPUT + | ENABLE_PROCESSED_OUTPUT + | ENABLE_WRAP_AT_EOL_OUTPUT); +#else /* * Make sure that stdin and stdout are non-blocking. */ fcntl(0, F_SETFL, O_NONBLOCK); fcntl(1, F_SETFL, O_NONBLOCK); +#endif /* * Perform the loop. @@ -160,9 +315,15 @@ run_ssl_engine(br_ssl_engine_context *cc, int fd, unsigned flags) for (;;) { unsigned st; int sendrec, recvrec, sendapp, recvapp; +#ifdef _WIN32 + HANDLE pfd[2]; + DWORD wt; +#else struct pollfd pfd[3]; int n; +#endif size_t u, k_fd, k_in, k_out; + int sendrec_ok, recvrec_ok, sendapp_ok, recvapp_ok; /* * Get current engine state. @@ -257,11 +418,74 @@ run_ssl_engine(br_ssl_engine_context *cc, int fd, unsigned flags) hsdetails = 1; } - k_fd = 0; - k_in = 0; - k_out = 0; + k_fd = (size_t)-1; + k_in = (size_t)-1; + k_out = (size_t)-1; u = 0; +#ifdef _WIN32 + /* + * If we recorded that we can send or receive data, and we + * want to do exactly that, then we don't wait; we just do + * it. + */ + recvapp_ok = 0; + sendrec_ok = 0; + recvrec_ok = 0; + sendapp_ok = 0; + + if (sendrec && can_send) { + sendrec_ok = 1; + } else if (recvrec && can_recv) { + recvrec_ok = 1; + } else if (recvapp) { + recvapp_ok = 1; + } else if (sendapp && in_avail_buffered(h_in, &bb)) { + sendapp_ok = 1; + } else { + /* + * If we cannot do I/O right away, then we must + * wait for some event, and try again. + */ + pfd[u] = (HANDLE)fd_event; + k_fd = u; + u ++; + if (sendapp) { + pfd[u] = h_in; + k_in = u; + u ++; + } + wt = WaitForMultipleObjectsEx(u, pfd, + FALSE, INFINITE, FALSE); + if (wt == WAIT_FAILED) { + fprintf(stderr, "ERROR:" + " WaitForMultipleObjectsEx()" + " failed with 0x%08lX", + (unsigned long)GetLastError()); + retcode = -2; + goto engine_exit; + } + if (wt == k_fd) { + WSANETWORKEVENTS e; + + if (WSAEnumNetworkEvents(fd, fd_event, &e)) { + fprintf(stderr, "ERROR:" + " WSAEnumNetworkEvents()" + " failed with %d\n", + WSAGetLastError()); + retcode = -2; + goto engine_exit; + } + if (e.lNetworkEvents & (FD_WRITE | FD_CLOSE)) { + can_send = 1; + } + if (e.lNetworkEvents & (FD_READ | FD_CLOSE)) { + can_recv = 1; + } + } + continue; + } +#else if (sendrec || recvrec) { pfd[u].fd = fd; pfd[u].revents = 0; @@ -313,96 +537,142 @@ run_ssl_engine(br_ssl_engine_context *cc, int fd, unsigned flags) } } + recvapp_ok = recvapp && (pfd[k_out].revents & POLLOUT) != 0; + sendrec_ok = sendrec && (pfd[k_fd].revents & POLLOUT) != 0; + recvrec_ok = recvrec && (pfd[k_fd].revents & POLLIN) != 0; + sendapp_ok = sendapp && (pfd[k_in].revents & POLLIN) != 0; +#endif + /* * We give preference to outgoing data, on stdout and on * the socket. */ - if (recvapp) { - if (pfd[k_out].revents & POLLOUT) { - unsigned char *buf; - size_t len; - ssize_t wlen; - - buf = br_ssl_engine_recvapp_buf(cc, &len); - wlen = write(1, buf, len); - if (wlen <= 0) { - if (verbose) { - fprintf(stderr, - "stdout closed...\n"); - } - retcode = -2; - goto engine_exit; + if (recvapp_ok) { + unsigned char *buf; + size_t len; +#ifdef _WIN32 + DWORD wlen; +#else + ssize_t wlen; +#endif + + buf = br_ssl_engine_recvapp_buf(cc, &len); +#ifdef _WIN32 + if (!WriteFile(h_out, buf, len, &wlen, NULL)) { + if (verbose) { + fprintf(stderr, "stdout closed...\n"); } - br_ssl_engine_recvapp_ack(cc, wlen); - continue; + retcode = -2; + goto engine_exit; } +#else + wlen = write(1, buf, len); + if (wlen <= 0) { + if (verbose) { + fprintf(stderr, "stdout closed...\n"); + } + retcode = -2; + goto engine_exit; + } +#endif + br_ssl_engine_recvapp_ack(cc, wlen); + continue; } - if (sendrec) { - if (pfd[k_fd].revents & POLLOUT) { - unsigned char *buf; - size_t len; - ssize_t wlen; - - buf = br_ssl_engine_sendrec_buf(cc, &len); - wlen = write(fd, buf, len); - if (wlen <= 0) { - if (verbose) { - fprintf(stderr, - "socket closed...\n"); - } - retcode = -1; - goto engine_exit; + if (sendrec_ok) { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(cc, &len); + wlen = send(fd, buf, len, 0); + if (wlen <= 0) { +#ifdef _WIN32 + int err; + + err = WSAGetLastError(); + if (err == EWOULDBLOCK + || err == WSAEWOULDBLOCK) + { + can_send = 0; + continue; } - if (trace) { - dump_blob("Outgoing bytes", buf, wlen); +#else + if (errno == EINTR || errno == EWOULDBLOCK) { + continue; } - br_ssl_engine_sendrec_ack(cc, wlen); - continue; +#endif + if (verbose) { + fprintf(stderr, "socket closed...\n"); + } + retcode = -1; + goto engine_exit; } + if (trace) { + dump_blob("Outgoing bytes", buf, wlen); + } + br_ssl_engine_sendrec_ack(cc, wlen); + continue; } - if (recvrec) { - if (pfd[k_fd].revents & POLLIN) { - unsigned char *buf; - size_t len; - ssize_t rlen; - - buf = br_ssl_engine_recvrec_buf(cc, &len); - rlen = read(fd, buf, len); - if (rlen <= 0) { - if (verbose) { - fprintf(stderr, - "socket closed...\n"); - } - retcode = -1; - goto engine_exit; + if (recvrec_ok) { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(cc, &len); + rlen = recv(fd, buf, len, 0); + if (rlen <= 0) { +#ifdef _WIN32 + int err; + + err = WSAGetLastError(); + if (err == EWOULDBLOCK + || err == WSAEWOULDBLOCK) + { + can_recv = 0; + continue; } - if (trace) { - dump_blob("Incoming bytes", buf, rlen); +#else + if (errno == EINTR || errno == EWOULDBLOCK) { + continue; } - br_ssl_engine_recvrec_ack(cc, rlen); - continue; +#endif + if (verbose) { + fprintf(stderr, "socket closed...\n"); + } + retcode = -1; + goto engine_exit; } + if (trace) { + dump_blob("Incoming bytes", buf, rlen); + } + br_ssl_engine_recvrec_ack(cc, rlen); + continue; } - if (sendapp) { - if (pfd[k_in].revents & POLLIN) { - unsigned char *buf; - size_t len; - ssize_t rlen; - - buf = br_ssl_engine_sendapp_buf(cc, &len); - rlen = read(0, buf, len); - if (rlen <= 0) { - if (verbose) { - fprintf(stderr, - "stdin closed...\n"); - } - br_ssl_engine_close(cc); - } else if (!run_command(cc, buf, rlen)) { - br_ssl_engine_sendapp_ack(cc, rlen); + if (sendapp_ok) { + unsigned char *buf; + size_t len; +#ifdef _WIN32 + int rlen; +#else + ssize_t rlen; +#endif + + buf = br_ssl_engine_sendapp_buf(cc, &len); +#ifdef _WIN32 + rlen = in_read_buffered(h_in, &bb, buf, len); +#else + rlen = read(0, buf, len); +#endif + if (rlen <= 0) { + if (verbose) { + fprintf(stderr, "stdin closed...\n"); } - br_ssl_engine_flush(cc, 0); - continue; + br_ssl_engine_close(cc); + } else if (!run_command(cc, buf, rlen)) { + br_ssl_engine_sendapp_ack(cc, rlen); } + br_ssl_engine_flush(cc, 0); + continue; } /* We should never reach that point. */ @@ -415,5 +685,10 @@ run_ssl_engine(br_ssl_engine_context *cc, int fd, unsigned flags) * Release allocated structures. */ engine_exit: +#ifdef _WIN32 + if (fd_event != WSA_INVALID_EVENT) { + WSACloseEvent(fd_event); + } +#endif return retcode; }