openssl: SSL_Read failed with SSL_ERROR_SYSCALL when read timeout
I use OpenSSL 1.1.1g connected to my socket client on Windows 8.1, I set read timeout (1 second) using
iVal = 1000;
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&iVal, sz);
SSL_read when timeout, return with -1 SSL_get_error return SSL_ERROR_SYSCALL , return message is
error:00000005:lib(0):func(0):DH lib
How do I know it is a timeout not a real error? read timout allow me to make ping and ping or checking thread etc (just an example) then back to read again, it is not error to close the socket and end it.
I expected SSL_Read return somthing like SSL_ERROR_WANT_READ to allow me to recall SSL_Read also there is kind of bug in SSL_get_error it reset error to 0 for WSAGetLastError
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 1
- Comments: 38 (11 by maintainers)
@zaher it looks like your issue has been resolved; can this be closed now?
I am making this a documentation issue as it would be nice if this was documented somewhere.
I see the problem. You should avoid using setsockopt() for timeouts. You can get wacky behavior when using setsockopt() and openssl. Chances are that you are going to be operating with a block cipher, instead of a streaming cipher. Remember that SSL_read just returns what openssl has finished decrypting, which may be different than how much has come over the socket. You’re forcing a timeout at the socket level.
First, call SSL_has_pending to see if openssl has some decrypted data to read. If it does, just call SSL_read. If it doesn’t, then use select() with an appropriate timeout. Then, if that doesn’t time out, you can check SSL_has_pending again to see if openssl has something to read. If this second call to SSL_has_pending fails, treat it as a timeout.
Realistically, the second call to SSL_has_pending is kind of redundant. If select() doesn’t time out, then openssl should have something ready for you to read.
On Wed, May 17, 2023 at 11:40 AM Youda008 @.***> wrote:
– John L Veazey
Understandable, but then OpenSSL shouldn’t touch errno / Windows last error, so that we can detect timeout by checking
errno == ETIMEDOUT || errno == EWOULDBLOCKorWSAGetLastError() == WSAETIMEDOUT.Using
select()works, but it’s rather an ugly workaround than a correct solution.OpenSSL should support the following workflow:
I guess my question is more along the lines about how
mnSockets.pasties together withmnOpenSSL.pas.In
mnOpenSSL.pas, I see a method calledTBIOStream.Read, but the only thing in it isSSL_readand I inmnSockets.pasI see a method calledTmnSocketStream.WaitToRead. Where in the code can I see these two interact?Edit2: I see where
selectis called, but there are problems with the code.https://github.com/parmaja/minilib/blob/master/socket/source/mnWinSockets.pasDo not set both
TimeVal.tv_secandTimeVal.tv_usec. Use one or the other.The code for determining a timeout does not match the C++ code I provided.
selectpasses anfd_setintoPSetReadandPsWrite. You need to pass innilinstead ofPSetWrite. We aren’t testing whether you can write to the socket.nil, instead of anfd_set. You need to pass something in here, since this parameter is how we can determine whether an error of some kind actually occurred (I’ll assume it is calledPSetError).selectfinishes, you need to callFD_ISSETforPSetReadandPSetError.FD_ISSET(socket, &read)returns true, there is no timeout.FD_ISSET(socket, &error)returns true, usegetsockoptand/orWSAGetLastErrorand/orerrno. If none of these returns useful information, assumeWSAECONNRESET.selectreturns zero,FD_ISSET(socket, &read)returns false, andFD_ISSET(socket, &error)returns false, assumeWSAETIMEDOUTLooking at, I do see
mnOpenSSL.paswhere a socket is passed in, but you don’t do anything with it outside ofSSL_set_fdandGetSocketError.You need to use
SSL_has_pendingandselectbefore callingSSL_read. Here’s a C++ example for usingselectto check for a read timeout. I’m not familiar enough to translate it to Delphi.