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)))