Hi,
When using OpenSSL on Windows I noticed that it's impossible to redirect
/ pipe commands directly to openssl s_client. The client apparently
keeps waiting for user input. For example, the following commands don' t
work (the connection times out or waits for user input).
echo Q | openssl s_client -connect www.google.com:443
openssl s_client -connect www.google.com:443 < file_containing_QUIT_and_EOL
In 2013 there was a discussion about this on the OpenSSL users
mailinglist (see
http://openssl.6102.n7.nabble.com/openssl-s-client-takes-over-30-seconds-to-complete-on-Windows-td45781.html
Other discussions can be found on StackOverflow, for example
http://stackoverflow.com/questions/16823068/gnuwin32-openssl-s-client-conn-to-websphere-mq-server-not-closing-at-eof-hangs
or
http://stackoverflow.com/questions/9450120/openssl-hangs-and-does-not-exit
Apparently the solution is already implemented in apps/s_client.c (line
1836-1840) with the WaitForSingleObject call.
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
/* Under Windows/DOS we make the assumption that
we can
* always write to the tty: therefore if we need to
* write to the tty we just fall through. Otherwise
* we timeout the select every second and see if there
* are any keypresses. Note: this is a hack, in a proper
* Windows application we wouldn't do this.
*/
i=0;
if(!write_tty) {
if(read_tty) {
tv.tv_sec = 1;
tv.tv_usec = 0;
i=select(width,(void *)&readfds,(void *)&writefds,
NULL,&tv);
#if defined(OPENSSL_SYS_WINCE) || defined(OPENSSL_SYS_MSDOS)
if(!i && (!_kbhit() || !read_tty) ) continue;
#else
if(!i && (!((_kbhit()) || (WAIT_OBJECT_0 ==
WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0))) || !read_tty) )
continue;
#endif
However the statement that fixes this (the WaitForSingleObject call )
(almost) never gets compiled on Windows, at least not when
OPENSSL_SYS_WINCE or OPENSSL_SYS_MSDOS are defined. The latter is (among
other places) defined in e_os2.h (line 119-126)
/* Anything that tries to look like Microsoft is "Windows" */
#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WINNT) ||
defined(OPENSSL_SYS_WINCE)
# undef OPENSSL_SYS_UNIX
# define OPENSSL_SYS_WINDOWS
# ifndef OPENSSL_SYS_MSDOS
# define OPENSSL_SYS_MSDOS
# endif
#endif
This effectively means that OPENSSL_SYS_MSDOS is (almost) always defined
when compiling for Windows, rendering the solution useless.
Please find attached a patch for the OpenSSL master branch, where
apps/s_client.c is modified. It removes the OPENSSL_SYS_MSDOS requirement.
After patching, input/output isn't blocked anymore and the following
commands work:
echo Q | openssl s_client -connect www.google.com:443
openssl s_client -connect www.google.com:443 < file_containing_QUIT_and_EOL
Tested under Windows when compiling on msys with mingw / mingw64. Please
note that I haven't tested compiling on Cygwin.
The WaitForSingleObject function is supported by Windows CE 5.0 and higher.
Hope this helps someone,
thanks for your consideration,
Peter Mosmans
diff --git a/apps/s_client.c b/apps/s_client.c
index e1be6a9..f2bc1fd 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -1833,7 +1833,7 @@ SSL_set_tlsext_status_ids(con, ids);
tv.tv_usec = 0;
i=select(width,(void *)&readfds,(void
*)&writefds,
NULL,&tv);
-#if defined(OPENSSL_SYS_WINCE) || defined(OPENSSL_SYS_MSDOS)
+#if defined(OPENSSL_SYS_WINCE)
if(!i && (!_kbhit() || !read_tty) )
continue;
#else
if(!i && (!((_kbhit()) ||
(WAIT_OBJECT_0 == WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0))) ||
!read_tty) ) continue;
@@ -2044,7 +2044,7 @@ printf("read=%d pending=%d
peek=%d\n",k,SSL_pending(con),SSL_peek(con,zbuf,10240
}
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
-#if defined(OPENSSL_SYS_WINCE) || defined(OPENSSL_SYS_MSDOS)
+#if defined(OPENSSL_SYS_WINCE)
else if (_kbhit())
#else
else if ((_kbhit()) || (WAIT_OBJECT_0 ==
WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0)))