static int s_sockexCanRead( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) { if( pSock->inbuffer ) return 1; else if( pSock->sd == HB_NO_SOCKET ) { hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE ); return -1; } else if( SSL_pending( HB_SSLSOCK_GET( pSock )->ssl ) ) { long len; if( pSock->buffer == NULL ) { if( pSock->readahead <= 0 ) pSock->readahead = HB_SSLSOCK_READAHEAD; pSock->buffer = ( HB_BYTE * ) hb_xgrab( pSock->readahead ); } len = hb_ssl_socketRead( HB_SSLSOCK_GET( pSock ), pSock->sd, pSock->buffer, pSock->readahead, 0 ); if( len > 0 ) { pSock->inbuffer = len; len = 1; } return ( int ) len; } return fBuffer ? 0 : hb_socketSelectRead( pSock->sd, timeout ); }
/* check if data can be read from extended socket, return 1, 0 or -1 to indicate error. If fBuffer parameter is set to true then only data already read from socket and stored in memory buffer should be checked without any timeout. Such call is executed just before inside hb_sockexSelect() just before call to low level socket select() function. */ static int s_sockexCanRead( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) { if( pSock->inbuffer > 0 ) return 1; else if( pSock->sd == HB_NO_SOCKET ) { hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE ); return -1; } return fBuffer ? 0 : hb_socketSelectRead( pSock->sd, timeout ); }
long hb_ssl_socketRead( PHB_SSLSTREAM pStream, HB_SOCKET sd, void * buffer, long len, HB_MAXINT timeout ) { HB_MAXUINT timer = timeout <= 0 ? 0 : hb_dateMilliSeconds(); long lRead = -1; int iToRead = -1; /* sd = SSL_get_rfd( pStream->ssl ); */ #if LONG_MAX > INT_MAX if( len > INT_MAX ) len = INT_MAX; #endif #if 0 while( ERR_get_error() != 0 ) { /* eat pending errors */ } #endif if( pStream->blocking ? timeout >= 0 : timeout < 0 ) { if( hb_socketSetBlockingIO( sd, timeout < 0 ) >= 0 ) pStream->blocking = ! pStream->blocking; } if( len > 0 ) { iToRead = SSL_pending( pStream->ssl ); if( iToRead <= 0 ) { iToRead = timeout < 0 ? 1 : hb_socketSelectRead( sd, timeout ); if( iToRead > 0 ) iToRead = ( int ) len; else if( iToRead == 0 ) hb_socketSetError( HB_SOCKET_ERR_TIMEOUT ); } else if( iToRead > len ) iToRead = ( int ) len; } while( iToRead > 0 ) { lRead = SSL_read( pStream->ssl, buffer, iToRead ); if( lRead > 0 ) hb_socketSetError( 0 ); else { int iError = SSL_get_error( pStream->ssl, ( int ) lRead ); switch( iError ) { case SSL_ERROR_ZERO_RETURN: hb_socketSetError( HB_SOCKET_ERR_PIPE ); lRead = 0; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if( hb_vmRequestQuery() == 0 ) { if( timeout > 0 ) { HB_MAXUINT timecurr = hb_dateMilliSeconds(); if( timecurr > timer ) timeout -= timecurr - timer; if( timeout > 0 ) { timer = timecurr; if( iError == SSL_ERROR_WANT_READ ) iError = hb_socketSelectRead( sd, timeout ); else iError = hb_socketSelectWrite( sd, timeout ); if( iError > 0 ) continue; else if( iError < 0 ) break; } } hb_socketSetError( HB_SOCKET_ERR_TIMEOUT ); break; } default: hb_socketSetError( HB_SSL_SOCK_ERROR_BASE + iError ); } } break; } return lRead; }
PHB_SSLSTREAM hb_ssl_socketNew( HB_SOCKET sd, SSL * ssl, HB_BOOL fServer, HB_MAXINT timeout, PHB_ITEM pSSL, int * piResult ) { int iResult; PHB_SSLSTREAM pStream; HB_MAXUINT timer; pStream = ( HB_SSLSTREAM * ) hb_xgrabz( sizeof( HB_SSLSTREAM ) ); timer = timeout <= 0 ? 0 : hb_dateMilliSeconds(); pStream->ssl = ssl; pStream->pSSL = pSSL ? hb_itemNew( pSSL ) : NULL; pStream->blocking = timeout < 0; if( hb_socketSetBlockingIO( sd, pStream->blocking ) < 0 ) pStream->blocking = ! pStream->blocking; SSL_set_mode( ssl, HB_SSL_MODE_AUTO_RETRY ); iResult = SSL_set_fd( ssl, sd ); /* Truncates `sd` on win64. OpenSSL bug: https://rt.openssl.org/Ticket/Display.html?id=1928&user=guest&pass=guest */ while( iResult == 1 ) { if( fServer ) iResult = SSL_accept( ssl ); else iResult = SSL_connect( ssl ); if( iResult != 1 && hb_vmRequestQuery() == 0 ) { int iError = SSL_get_error( ssl, iResult ); if( iError == SSL_ERROR_WANT_READ || iError == SSL_ERROR_WANT_WRITE ) { if( timeout < 0 ) { iResult = 1; continue; } else if( timeout > 0 ) { HB_MAXUINT timecurr = hb_dateMilliSeconds(); if( timecurr > timer ) { timeout -= timecurr - timer; if( timeout < 0 ) timeout = 0; timer = timecurr; } if( timeout > 0 ) { if( iError == SSL_ERROR_WANT_READ ) iError = hb_socketSelectRead( sd, timeout ); else iError = hb_socketSelectWrite( sd, timeout ); if( iError > 0 ) { iResult = 1; continue; } } } hb_socketSetError( HB_SOCKET_ERR_TIMEOUT ); } } break; } if( iResult != 1 ) { hb_ssl_socketClose( pStream ); pStream = NULL; } else pStream->blocking = hb_socketSetBlockingIO( sd, HB_FALSE ) < 0; if( piResult ) *piResult = iResult; return pStream; }
long hb_ssl_socketWrite( PHB_SSLSTREAM pStream, HB_SOCKET sd, const void * buffer, long len, HB_MAXINT timeout, long * plast ) { HB_MAXUINT timer = timeout <= 0 ? 0 : hb_dateMilliSeconds(); long lWritten = 0, lWr = 0; /* sd = SSL_get_wfd( pStream->ssl ); */ #if LONG_MAX > INT_MAX if( len > INT_MAX ) len = INT_MAX; #endif #if 0 while( ERR_get_error() != 0 ) { /* eat pending errors */ } #endif if( pStream->blocking ? timeout >= 0 : timeout < 0 ) { if( hb_socketSetBlockingIO( sd, timeout < 0 ) >= 0 ) pStream->blocking = ! pStream->blocking; } while( len > 0 ) { lWr = SSL_write( pStream->ssl, buffer, ( int ) len ); if( plast ) *plast = lWr; if( lWr > 0 ) { lWritten += lWr; len -= lWr; buffer = ( const char * ) buffer + lWr; hb_socketSetError( 0 ); } else { int iError = SSL_get_error( pStream->ssl, ( int ) lWr ); switch( iError ) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: if( hb_vmRequestQuery() == 0 ) { if( timeout > 0 ) { HB_MAXUINT timecurr = hb_dateMilliSeconds(); if( timecurr > timer ) timeout -= timecurr - timer; if( timeout > 0 ) { timer = timecurr; if( iError == SSL_ERROR_WANT_READ ) iError = hb_socketSelectRead( sd, timeout ); else iError = hb_socketSelectWrite( sd, timeout ); if( iError > 0 ) continue; } else iError = 0; } else iError = 0; if( lWritten == 0 && iError == 0 ) hb_socketSetError( HB_SOCKET_ERR_TIMEOUT ); break; } default: hb_socketSetError( HB_SSL_SOCK_ERROR_BASE + iError ); } break; } } return lWritten != 0 ? lWritten : lWr; }
PHB_SSLSTREAM hb_ssl_socketNew( HB_SOCKET sd, SSL * ssl, HB_BOOL fServer, HB_MAXINT timeout, int * piResult ) { int iResult; PHB_SSLSTREAM pStream; HB_MAXUINT timer; pStream = ( HB_SSLSTREAM * ) hb_xgrabz( sizeof( HB_SSLSTREAM ) ); timer = timeout <= 0 ? 0 : hb_dateMilliSeconds(); pStream->ssl = ssl; pStream->pSSL = hb_itemNew( hb_param( 2, HB_IT_POINTER ) ); pStream->blocking = timeout < 0; if( hb_socketSetBlockingIO( sd, pStream->blocking ) < 0 ) pStream->blocking = ! pStream->blocking; SSL_set_mode( ssl, HB_SSL_MODE_AUTO_RETRY ); iResult = SSL_set_fd( ssl, sd ); while( iResult == 1 ) { if( fServer ) iResult = SSL_accept( ssl ); else iResult = SSL_connect( ssl ); if( iResult != 1 && hb_vmRequestQuery() == 0 ) { int iError = SSL_get_error( ssl, iResult ); if( iError == SSL_ERROR_WANT_READ || iError == SSL_ERROR_WANT_WRITE ) { if( timeout < 0 ) { iResult = 1; continue; } else if( timeout > 0 ) { HB_MAXUINT timecurr = hb_dateMilliSeconds(); if( timecurr > timer ) { timeout -= timecurr - timer; timer = timecurr; } if( timeout > 0 ) { if( iError == SSL_ERROR_WANT_READ ) iError = hb_socketSelectRead( sd, timeout ); else iError = hb_socketSelectWrite( sd, timeout ); if( iError > 0 ) { iResult = 1; continue; } } } hb_socketSetError( HB_SOCKET_ERR_TIMEOUT ); } } break; } if( iResult != 1 ) { hb_ssl_socketClose( pStream ); pStream = NULL; } else pStream->blocking = hb_socketSetBlockingIO( sd, HB_FALSE ) < 0; if( piResult ) *piResult = iResult; return pStream; }