xref: /freebsd/contrib/bearssl/tools/sslio.c (revision 13ea0450a9c8742119d36f3bf8f47accdce46e54)
1 /*
2  * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdint.h>
29 #include <errno.h>
30 
31 #ifdef _WIN32
32 #include <winsock2.h>
33 #include <ws2tcpip.h>
34 #else
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netdb.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <poll.h>
43 
44 #define SOCKET           int
45 #define INVALID_SOCKET   (-1)
46 #endif
47 
48 #include "brssl.h"
49 
50 static void
51 dump_blob(const char *name, const void *data, size_t len)
52 {
53 	const unsigned char *buf;
54 	size_t u;
55 
56 	buf = data;
57 	fprintf(stderr, "%s (len = %lu)", name, (unsigned long)len);
58 	for (u = 0; u < len; u ++) {
59 		if ((u & 15) == 0) {
60 			fprintf(stderr, "\n%08lX  ", (unsigned long)u);
61 		} else if ((u & 7) == 0) {
62 			fprintf(stderr, " ");
63 		}
64 		fprintf(stderr, " %02x", buf[u]);
65 	}
66 	fprintf(stderr, "\n");
67 }
68 
69 /*
70  * Inspect the provided data in case it is a "command" to trigger a
71  * special behaviour. If the command is recognised, then it is executed
72  * and this function returns 1. Otherwise, this function returns 0.
73  */
74 static int
75 run_command(br_ssl_engine_context *cc, unsigned char *buf, size_t len)
76 {
77 	/*
78 	 * A single static slot for saving session parameters.
79 	 */
80 	static br_ssl_session_parameters slot;
81 	static int slot_used = 0;
82 
83 	size_t u;
84 
85 	if (len < 2 || len > 3) {
86 		return 0;
87 	}
88 	if (len == 3 && (buf[1] != '\r' || buf[2] != '\n')) {
89 		return 0;
90 	}
91 	if (len == 2 && buf[1] != '\n') {
92 		return 0;
93 	}
94 	switch (buf[0]) {
95 	case 'Q':
96 		fprintf(stderr, "closing...\n");
97 		br_ssl_engine_close(cc);
98 		return 1;
99 	case 'R':
100 		if (br_ssl_engine_renegotiate(cc)) {
101 			fprintf(stderr, "renegotiating...\n");
102 		} else {
103 			fprintf(stderr, "not renegotiating.\n");
104 		}
105 		return 1;
106 	case 'F':
107 		/*
108 		 * Session forget is nominally client-only. But the
109 		 * session parameters are in the engine structure, which
110 		 * is the first field of the client context, so the cast
111 		 * still works properly. On the server, this forgetting
112 		 * has no effect.
113 		 */
114 		fprintf(stderr, "forgetting session...\n");
115 		br_ssl_client_forget_session((br_ssl_client_context *)cc);
116 		return 1;
117 	case 'S':
118 		fprintf(stderr, "saving session parameters...\n");
119 		br_ssl_engine_get_session_parameters(cc, &slot);
120 		fprintf(stderr, "  id = ");
121 		for (u = 0; u < slot.session_id_len; u ++) {
122 			fprintf(stderr, "%02X", slot.session_id[u]);
123 		}
124 		fprintf(stderr, "\n");
125 		slot_used = 1;
126 		return 1;
127 	case 'P':
128 		if (slot_used) {
129 			fprintf(stderr, "restoring session parameters...\n");
130 			fprintf(stderr, "  id = ");
131 			for (u = 0; u < slot.session_id_len; u ++) {
132 				fprintf(stderr, "%02X", slot.session_id[u]);
133 			}
134 			fprintf(stderr, "\n");
135 			br_ssl_engine_set_session_parameters(cc, &slot);
136 			return 1;
137 		}
138 		return 0;
139 	default:
140 		return 0;
141 	}
142 }
143 
144 #ifdef _WIN32
145 
146 typedef struct {
147 	unsigned char buf[1024];
148 	size_t ptr, len;
149 } in_buffer;
150 
151 static int
152 in_return_bytes(in_buffer *bb, unsigned char *buf, size_t len)
153 {
154 	if (bb->ptr < bb->len) {
155 		size_t clen;
156 
157 		if (buf == NULL) {
158 			return 1;
159 		}
160 		clen = bb->len - bb->ptr;
161 		if (clen > len) {
162 			clen = len;
163 		}
164 		memcpy(buf, bb->buf + bb->ptr, clen);
165 		bb->ptr += clen;
166 		if (bb->ptr == bb->len) {
167 			bb->ptr = bb->len = 0;
168 		}
169 		return (int)clen;
170 	}
171 	return 0;
172 }
173 
174 /*
175  * A buffered version of in_read(), using a buffer to return only
176  * full lines when feasible.
177  */
178 static int
179 in_read_buffered(HANDLE h_in, in_buffer *bb, unsigned char *buf, size_t len)
180 {
181 	int n;
182 
183 	if (len == 0) {
184 		return 0;
185 	}
186 	n = in_return_bytes(bb, buf, len);
187 	if (n != 0) {
188 		return n;
189 	}
190 	for (;;) {
191 		INPUT_RECORD inrec;
192 		DWORD v;
193 
194 		if (!PeekConsoleInput(h_in, &inrec, 1, &v)) {
195 			fprintf(stderr, "ERROR: PeekConsoleInput()"
196 				" failed with 0x%08lX\n",
197 				(unsigned long)GetLastError());
198 			return -1;
199 		}
200 		if (v == 0) {
201 			return 0;
202 		}
203 		if (!ReadConsoleInput(h_in, &inrec, 1, &v)) {
204 			fprintf(stderr, "ERROR: ReadConsoleInput()"
205 				" failed with 0x%08lX\n",
206 				(unsigned long)GetLastError());
207 			return -1;
208 		}
209 		if (v == 0) {
210 			return 0;
211 		}
212 		if (inrec.EventType == KEY_EVENT
213 			&& inrec.Event.KeyEvent.bKeyDown)
214 		{
215 			int c;
216 
217 			c = inrec.Event.KeyEvent.uChar.AsciiChar;
218 			if (c == '\n' || c == '\r' || c == '\t'
219 				|| (c >= 32 && c != 127))
220 			{
221 				if (c == '\r') {
222 					c = '\n';
223 				}
224 				bb->buf[bb->ptr ++] = (unsigned char)c;
225 				printf("%c", c);
226 				fflush(stdout);
227 				bb->len = bb->ptr;
228 				if (bb->len == sizeof bb->buf || c == '\n') {
229 					bb->ptr = 0;
230 					return in_return_bytes(bb, buf, len);
231 				}
232 			}
233 		}
234 	}
235 }
236 
237 static int
238 in_avail_buffered(HANDLE h_in, in_buffer *bb)
239 {
240 	return in_read_buffered(h_in, bb, NULL, 1);
241 }
242 
243 #endif
244 
245 /* see brssl.h */
246 int
247 run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags)
248 {
249 	int hsdetails;
250 	int retcode;
251 	int verbose;
252 	int trace;
253 #ifdef _WIN32
254 	WSAEVENT fd_event;
255 	int can_send, can_recv;
256 	HANDLE h_in, h_out;
257 	in_buffer bb;
258 #endif
259 
260 	hsdetails = 0;
261 	retcode = 0;
262 	verbose = (flags & RUN_ENGINE_VERBOSE) != 0;
263 	trace = (flags & RUN_ENGINE_TRACE) != 0;
264 
265 	/*
266 	 * Print algorithm details.
267 	 */
268 	if (verbose) {
269 		const char *rngname;
270 
271 		fprintf(stderr, "Algorithms:\n");
272 		br_prng_seeder_system(&rngname);
273 		fprintf(stderr, "   RNG:           %s\n", rngname);
274 		if (cc->iaes_cbcenc != 0) {
275 			fprintf(stderr, "   AES/CBC (enc): %s\n",
276 				get_algo_name(cc->iaes_cbcenc, 0));
277 		}
278 		if (cc->iaes_cbcdec != 0) {
279 			fprintf(stderr, "   AES/CBC (dec): %s\n",
280 				get_algo_name(cc->iaes_cbcdec, 0));
281 		}
282 		if (cc->iaes_ctr != 0) {
283 			fprintf(stderr, "   AES/CTR:       %s\n",
284 				get_algo_name(cc->iaes_cbcdec, 0));
285 		}
286 		if (cc->iaes_ctrcbc != 0) {
287 			fprintf(stderr, "   AES/CCM:       %s\n",
288 				get_algo_name(cc->iaes_ctrcbc, 0));
289 		}
290 		if (cc->ides_cbcenc != 0) {
291 			fprintf(stderr, "   DES/CBC (enc): %s\n",
292 				get_algo_name(cc->ides_cbcenc, 0));
293 		}
294 		if (cc->ides_cbcdec != 0) {
295 			fprintf(stderr, "   DES/CBC (dec): %s\n",
296 				get_algo_name(cc->ides_cbcdec, 0));
297 		}
298 		if (cc->ighash != 0) {
299 			fprintf(stderr, "   GHASH (GCM):   %s\n",
300 				get_algo_name(cc->ighash, 0));
301 		}
302 		if (cc->ichacha != 0) {
303 			fprintf(stderr, "   ChaCha20:      %s\n",
304 				get_algo_name(cc->ichacha, 0));
305 		}
306 		if (cc->ipoly != 0) {
307 			fprintf(stderr, "   Poly1305:      %s\n",
308 				get_algo_name(cc->ipoly, 0));
309 		}
310 		if (cc->iec != 0) {
311 			fprintf(stderr, "   EC:            %s\n",
312 				get_algo_name(cc->iec, 0));
313 		}
314 		if (cc->iecdsa != 0) {
315 			fprintf(stderr, "   ECDSA:         %s\n",
316 				get_algo_name(cc->iecdsa, 0));
317 		}
318 		if (cc->irsavrfy != 0) {
319 			fprintf(stderr, "   RSA (vrfy):    %s\n",
320 				get_algo_name(cc->irsavrfy, 0));
321 		}
322 	}
323 
324 #ifdef _WIN32
325 	fd_event = WSA_INVALID_EVENT;
326 	can_send = 0;
327 	can_recv = 0;
328 	bb.ptr = bb.len = 0;
329 #endif
330 
331 	/*
332 	 * On Unix systems, we need to follow three descriptors:
333 	 * standard input (0), standard output (1), and the socket
334 	 * itself (for both read and write). This is done with a poll()
335 	 * call.
336 	 *
337 	 * On Windows systems, we use WSAEventSelect() to associate
338 	 * an event handle with the network activity, and we use
339 	 * WaitForMultipleObjectsEx() on that handle and the standard
340 	 * input handle, when appropriate. Standard output is assumed
341 	 * to be always writeable, and standard input to be the console;
342 	 * this does not work well (or at all) with redirections (to
343 	 * pipes or files) but it should be enough for a debug tool
344 	 * (TODO: make something that handles redirections as well).
345 	 */
346 
347 #ifdef _WIN32
348 	fd_event = WSACreateEvent();
349 	if (fd_event == WSA_INVALID_EVENT) {
350 		fprintf(stderr, "ERROR: WSACreateEvent() failed with %d\n",
351 			WSAGetLastError());
352 		retcode = -2;
353 		goto engine_exit;
354 	}
355 	WSAEventSelect(fd, fd_event, FD_READ | FD_WRITE | FD_CLOSE);
356 	h_in = GetStdHandle(STD_INPUT_HANDLE);
357 	h_out = GetStdHandle(STD_OUTPUT_HANDLE);
358 	SetConsoleMode(h_in, ENABLE_ECHO_INPUT
359 		| ENABLE_LINE_INPUT
360 		| ENABLE_PROCESSED_INPUT
361 		| ENABLE_PROCESSED_OUTPUT
362 		| ENABLE_WRAP_AT_EOL_OUTPUT);
363 #else
364 	/*
365 	 * Make sure that stdin and stdout are non-blocking.
366 	 */
367 	fcntl(0, F_SETFL, O_NONBLOCK);
368 	fcntl(1, F_SETFL, O_NONBLOCK);
369 #endif
370 
371 	/*
372 	 * Perform the loop.
373 	 */
374 	for (;;) {
375 		unsigned st;
376 		int sendrec, recvrec, sendapp, recvapp;
377 #ifdef _WIN32
378 		HANDLE pfd[2];
379 		DWORD wt;
380 #else
381 		struct pollfd pfd[3];
382 		int n;
383 #endif
384 		size_t u, k_fd, k_in, k_out;
385 		int sendrec_ok, recvrec_ok, sendapp_ok, recvapp_ok;
386 
387 		/*
388 		 * Get current engine state.
389 		 */
390 		st = br_ssl_engine_current_state(cc);
391 		if (st == BR_SSL_CLOSED) {
392 			int err;
393 
394 			err = br_ssl_engine_last_error(cc);
395 			if (err == BR_ERR_OK) {
396 				if (verbose) {
397 					fprintf(stderr,
398 						"SSL closed normally\n");
399 				}
400 				retcode = 0;
401 				goto engine_exit;
402 			} else {
403 				fprintf(stderr, "ERROR: SSL error %d", err);
404 				retcode = err;
405 				if (err >= BR_ERR_SEND_FATAL_ALERT) {
406 					err -= BR_ERR_SEND_FATAL_ALERT;
407 					fprintf(stderr,
408 						" (sent alert %d)\n", err);
409 				} else if (err >= BR_ERR_RECV_FATAL_ALERT) {
410 					err -= BR_ERR_RECV_FATAL_ALERT;
411 					fprintf(stderr,
412 						" (received alert %d)\n", err);
413 				} else {
414 					const char *ename;
415 
416 					ename = find_error_name(err, NULL);
417 					if (ename == NULL) {
418 						ename = "unknown";
419 					}
420 					fprintf(stderr, " (%s)\n", ename);
421 				}
422 				goto engine_exit;
423 			}
424 		}
425 
426 		/*
427 		 * Compute descriptors that must be polled, depending
428 		 * on engine state.
429 		 */
430 		sendrec = ((st & BR_SSL_SENDREC) != 0);
431 		recvrec = ((st & BR_SSL_RECVREC) != 0);
432 		sendapp = ((st & BR_SSL_SENDAPP) != 0);
433 		recvapp = ((st & BR_SSL_RECVAPP) != 0);
434 		if (verbose && sendapp && !hsdetails) {
435 			char csn[80];
436 			const char *pname;
437 
438 			fprintf(stderr, "Handshake completed\n");
439 			fprintf(stderr, "   version:               ");
440 			switch (cc->session.version) {
441 			case BR_SSL30:
442 				fprintf(stderr, "SSL 3.0");
443 				break;
444 			case BR_TLS10:
445 				fprintf(stderr, "TLS 1.0");
446 				break;
447 			case BR_TLS11:
448 				fprintf(stderr, "TLS 1.1");
449 				break;
450 			case BR_TLS12:
451 				fprintf(stderr, "TLS 1.2");
452 				break;
453 			default:
454 				fprintf(stderr, "unknown (0x%04X)",
455 					(unsigned)cc->session.version);
456 				break;
457 			}
458 			fprintf(stderr, "\n");
459 			get_suite_name_ext(
460 				cc->session.cipher_suite, csn, sizeof csn);
461 			fprintf(stderr, "   cipher suite:          %s\n", csn);
462 			if (uses_ecdhe(cc->session.cipher_suite)) {
463 				get_curve_name_ext(
464 					br_ssl_engine_get_ecdhe_curve(cc),
465 					csn, sizeof csn);
466 				fprintf(stderr,
467 					"   ECDHE curve:           %s\n", csn);
468 			}
469 			fprintf(stderr, "   secure renegotiation:  %s\n",
470 				cc->reneg == 1 ? "no" : "yes");
471 			pname = br_ssl_engine_get_selected_protocol(cc);
472 			if (pname != NULL) {
473 				fprintf(stderr,
474 					"   protocol name (ALPN):  %s\n",
475 					pname);
476 			}
477 			hsdetails = 1;
478 		}
479 
480 		k_fd = (size_t)-1;
481 		k_in = (size_t)-1;
482 		k_out = (size_t)-1;
483 
484 		u = 0;
485 #ifdef _WIN32
486 		/*
487 		 * If we recorded that we can send or receive data, and we
488 		 * want to do exactly that, then we don't wait; we just do
489 		 * it.
490 		 */
491 		recvapp_ok = 0;
492 		sendrec_ok = 0;
493 		recvrec_ok = 0;
494 		sendapp_ok = 0;
495 
496 		if (sendrec && can_send) {
497 			sendrec_ok = 1;
498 		} else if (recvrec && can_recv) {
499 			recvrec_ok = 1;
500 		} else if (recvapp) {
501 			recvapp_ok = 1;
502 		} else if (sendapp && in_avail_buffered(h_in, &bb)) {
503 			sendapp_ok = 1;
504 		} else {
505 			/*
506 			 * If we cannot do I/O right away, then we must
507 			 * wait for some event, and try again.
508 			 */
509 			pfd[u] = (HANDLE)fd_event;
510 			k_fd = u;
511 			u ++;
512 			if (sendapp) {
513 				pfd[u] = h_in;
514 				k_in = u;
515 				u ++;
516 			}
517 			wt = WaitForMultipleObjectsEx(u, pfd,
518 				FALSE, INFINITE, FALSE);
519 			if (wt == WAIT_FAILED) {
520 				fprintf(stderr, "ERROR:"
521 					" WaitForMultipleObjectsEx()"
522 					" failed with 0x%08lX",
523 					(unsigned long)GetLastError());
524 				retcode = -2;
525 				goto engine_exit;
526 			}
527 			if (wt == k_fd) {
528 				WSANETWORKEVENTS e;
529 
530 				if (WSAEnumNetworkEvents(fd, fd_event, &e)) {
531 					fprintf(stderr, "ERROR:"
532 						" WSAEnumNetworkEvents()"
533 						" failed with %d\n",
534 						WSAGetLastError());
535 					retcode = -2;
536 					goto engine_exit;
537 				}
538 				if (e.lNetworkEvents & (FD_WRITE | FD_CLOSE)) {
539 					can_send = 1;
540 				}
541 				if (e.lNetworkEvents & (FD_READ | FD_CLOSE)) {
542 					can_recv = 1;
543 				}
544 			}
545 			continue;
546 		}
547 #else
548 		if (sendrec || recvrec) {
549 			pfd[u].fd = fd;
550 			pfd[u].revents = 0;
551 			pfd[u].events = 0;
552 			if (sendrec) {
553 				pfd[u].events |= POLLOUT;
554 			}
555 			if (recvrec) {
556 				pfd[u].events |= POLLIN;
557 			}
558 			k_fd = u;
559 			u ++;
560 		}
561 		if (sendapp) {
562 			pfd[u].fd = 0;
563 			pfd[u].revents = 0;
564 			pfd[u].events = POLLIN;
565 			k_in = u;
566 			u ++;
567 		}
568 		if (recvapp) {
569 			pfd[u].fd = 1;
570 			pfd[u].revents = 0;
571 			pfd[u].events = POLLOUT;
572 			k_out = u;
573 			u ++;
574 		}
575 		n = poll(pfd, u, -1);
576 		if (n < 0) {
577 			if (errno == EINTR) {
578 				continue;
579 			}
580 			perror("ERROR: poll()");
581 			retcode = -2;
582 			goto engine_exit;
583 		}
584 		if (n == 0) {
585 			continue;
586 		}
587 
588 		/*
589 		 * We transform closures/errors into read+write accesses
590 		 * so as to force the read() or write() call that will
591 		 * detect the situation.
592 		 */
593 		while (u -- > 0) {
594 			if (pfd[u].revents & (POLLERR | POLLHUP)) {
595 				pfd[u].revents |= POLLIN | POLLOUT;
596 			}
597 		}
598 
599 		recvapp_ok = recvapp && (pfd[k_out].revents & POLLOUT) != 0;
600 		sendrec_ok = sendrec && (pfd[k_fd].revents & POLLOUT) != 0;
601 		recvrec_ok = recvrec && (pfd[k_fd].revents & POLLIN) != 0;
602 		sendapp_ok = sendapp && (pfd[k_in].revents & POLLIN) != 0;
603 #endif
604 
605 		/*
606 		 * We give preference to outgoing data, on stdout and on
607 		 * the socket.
608 		 */
609 		if (recvapp_ok) {
610 			unsigned char *buf;
611 			size_t len;
612 #ifdef _WIN32
613 			DWORD wlen;
614 #else
615 			ssize_t wlen;
616 #endif
617 
618 			buf = br_ssl_engine_recvapp_buf(cc, &len);
619 #ifdef _WIN32
620 			if (!WriteFile(h_out, buf, len, &wlen, NULL)) {
621 				if (verbose) {
622 					fprintf(stderr, "stdout closed...\n");
623 				}
624 				retcode = -2;
625 				goto engine_exit;
626 			}
627 #else
628 			wlen = write(1, buf, len);
629 			if (wlen <= 0) {
630 				if (verbose) {
631 					fprintf(stderr, "stdout closed...\n");
632 				}
633 				retcode = -2;
634 				goto engine_exit;
635 			}
636 #endif
637 			br_ssl_engine_recvapp_ack(cc, wlen);
638 			continue;
639 		}
640 		if (sendrec_ok) {
641 			unsigned char *buf;
642 			size_t len;
643 			int wlen;
644 
645 			buf = br_ssl_engine_sendrec_buf(cc, &len);
646 			wlen = send(fd, buf, len, 0);
647 			if (wlen <= 0) {
648 #ifdef _WIN32
649 				int err;
650 
651 				err = WSAGetLastError();
652 				if (err == EWOULDBLOCK
653 					|| err == WSAEWOULDBLOCK)
654 				{
655 					can_send = 0;
656 					continue;
657 				}
658 #else
659 				if (errno == EINTR || errno == EWOULDBLOCK) {
660 					continue;
661 				}
662 #endif
663 				if (verbose) {
664 					fprintf(stderr, "socket closed...\n");
665 				}
666 				retcode = -1;
667 				goto engine_exit;
668 			}
669 			if (trace) {
670 				dump_blob("Outgoing bytes", buf, wlen);
671 			}
672 			br_ssl_engine_sendrec_ack(cc, wlen);
673 			continue;
674 		}
675 		if (recvrec_ok) {
676 			unsigned char *buf;
677 			size_t len;
678 			int rlen;
679 
680 			buf = br_ssl_engine_recvrec_buf(cc, &len);
681 			rlen = recv(fd, buf, len, 0);
682 			if (rlen == 0) {
683 				if (verbose) {
684 					fprintf(stderr, "socket closed...\n");
685 				}
686 				retcode = -1;
687 				goto engine_exit;
688 			}
689 			if (rlen < 0) {
690 #ifdef _WIN32
691 				int err;
692 
693 				err = WSAGetLastError();
694 				if (err == EWOULDBLOCK
695 					|| err == WSAEWOULDBLOCK)
696 				{
697 					can_recv = 0;
698 					continue;
699 				}
700 #else
701 				if (errno == EINTR || errno == EWOULDBLOCK) {
702 					continue;
703 				}
704 #endif
705 				if (verbose) {
706 					fprintf(stderr, "socket broke...\n");
707 				}
708 				retcode = -1;
709 				goto engine_exit;
710 			}
711 			if (trace) {
712 				dump_blob("Incoming bytes", buf, rlen);
713 			}
714 			br_ssl_engine_recvrec_ack(cc, rlen);
715 			continue;
716 		}
717 		if (sendapp_ok) {
718 			unsigned char *buf;
719 			size_t len;
720 #ifdef _WIN32
721 			int rlen;
722 #else
723 			ssize_t rlen;
724 #endif
725 
726 			buf = br_ssl_engine_sendapp_buf(cc, &len);
727 #ifdef _WIN32
728 			rlen = in_read_buffered(h_in, &bb, buf, len);
729 #else
730 			rlen = read(0, buf, len);
731 #endif
732 			if (rlen <= 0) {
733 				if (verbose) {
734 					fprintf(stderr, "stdin closed...\n");
735 				}
736 				br_ssl_engine_close(cc);
737 			} else if (!run_command(cc, buf, rlen)) {
738 				br_ssl_engine_sendapp_ack(cc, rlen);
739 			}
740 			br_ssl_engine_flush(cc, 0);
741 			continue;
742 		}
743 
744 		/* We should never reach that point. */
745 		fprintf(stderr, "ERROR: poll() misbehaves\n");
746 		retcode = -2;
747 		goto engine_exit;
748 	}
749 
750 	/*
751 	 * Release allocated structures.
752 	 */
753 engine_exit:
754 #ifdef _WIN32
755 	if (fd_event != WSA_INVALID_EVENT) {
756 		WSACloseEvent(fd_event);
757 	}
758 #endif
759 	return retcode;
760 }
761