We encountered a situation within NT 4.0 (using Visual C++ 6.0) where data
was
sent to the socket layer of openssl correctly, but openssl never receives
it.
Unix appears to be unaffected. What makes this especially peculiar, is that
we
configured the socket to be blocking, but somehow, it becomes non-blocking.
Nowhere in our code did we use WSAEventSelect or WSAAsyncSelect or
temporarily
use a non-blocking socket.
The client sends the client hello message and the server responds with the
server hello message. The format and certificates of these messages are
correct. For some reason, after a call to SSL_write recv never gets the data
from the server. All the client receives is a -1 and is forced to give up
without the option of trying to receive data again.
We traced the problem to the sock_read function within
openssl-0.9.4\crypto\bio\bss_sock.c.
179 #ifndef BIO_FD
180 static int sock_read(BIO *b, char *out, int outl)
181 #else
182 static int fd_read(BIO *b, char *out,int outl)
183 #endif
184 {
185 int ret=0;
186
187 if (out != NULL)
188 {
189 #ifndef BIO_FD
190 clear_socket_error();
191 ret=readsocket(b->num,out,outl);
192 #else
193 clear_sys_error();
194 ret=read(b->num,out,outl);
195 #endif
196 BIO_clear_retry_flags(b);
197 if (ret <= 0)
198 {
199 #ifndef BIO_FD
200 if (BIO_sock_should_retry(ret))
201 #else
202 if (BIO_fd_should_retry(ret))
203 #endif
204 BIO_set_retry_read(b);
205 }
206 }
207 return(ret);
208 }
The function readsocket is actually recv if BIO_FD is not defined.
From openssl-0.9.4\inc32\openssl\e_os.h(119):
#define readsocket(s,b,n) recv((s),(b),(n),0)
Under closer examination, when openssl executes the ssl3_server_hello
portion
of the protocol, readsocket (line 191) returns with -1 and outl > 0 with no
bytes read within variable out. While executing line 200, openssl discovers
that the error is WSAEWOULDBLOCK (10035), and sets the appropriate flags for
retrying the socket. Instead of immediately retrying the socket, each
function
called in the stack within openssl, passes -1 to the function calling it all
the way up to SSL_write. If recv waited a few milliseconds more, the data
would
be available.
After receiving a -1 from SSL_write, if someone tries calling it again, the
same error occurs. By calling it again, the whole process is started rather
than retrying to receive the last message. Because of these facts, the
following temporary solution was developed:
66A* #if defined(MSDOS) || defined(WINDOWS)
66B* #include <time.h>
66C* #endif
179 #ifndef BIO_FD
180 static int sock_read(BIO *b, char *out, int outl)
181 #else
182 static int fd_read(BIO *b, char *out,int outl)
183 #endif
184 {
185 int ret=0;
185A* #if defined(MSDOS) || defined(WINDOWS)
185B* int err = 0;
185C* clock_t referencetime = clock();
185D* clock_t currenttime = clock();
185E* double difftime = 0;
185F* double timeout = 100; /* in ms */
185G* #endif
186
187 if (out != NULL)
188 {
189 #ifndef BIO_FD
189A* #if defined(MSDOS) || defined(WINDOWS)
189B* do
189C*
189D* #endif
190 clear_socket_error();
191 ret=readsocket(b->num,out,outl);
191A* #if defined(MSDOS) || defined(WINDOWS)
191B* err = get_last_socket_error();
191C* currenttime = clock();
191D* difftime = (double)(((currenttime - referencetime) * 100)
/
CLOCKS_PER_SEC);
191E* }
191F* while((err != 0)&&(timeout > difftime));
191G* #endif
192 #else
193 clear_sys_error();
194 ret=read(b->num,out,outl);
195 #endif
196 BIO_clear_retry_flags(b);
197 if (ret <= 0)
198 {
199 #ifndef BIO_FD
200 if (BIO_sock_should_retry(ret))
201 #else
202 if (BIO_fd_should_retry(ret))
203 #endif
204 BIO_set_retry_read(b);
205 }
206 }
207 return(ret);
208 }
We verified that the client sends the client hello message and the server
responds with the server hello message. The format and certificates of these
messages are correct. For some reason, recv never gets the data from the
server. All the client gets is a -1 and is forced to give up without the
option
of trying to receive data again.
In other words: with the socket in a non-blocking mode, if this section of
code
generates a EWOULDBLOCK error, it does not retry reading. All it does is set
a
flag and report an error up the call stack.
There may be a better way to handle this; however, this is what we had to do
to make our application work.
Paul Kudlawiec
Software Engineer
[EMAIL PROTECTED]
______________________________________________________________________
OpenSSL Project http://www.openssl.org
Development Mailing List [EMAIL PROTECTED]
Automated List Manager [EMAIL PROTECTED]