* include/hbsocket.h
* src/rtl/hbsockhb.c
+ added socket filters to standard socket API.
At C level hb_sockex*() function with PHB_SOCKEX handler can be
used to operate on socket filters. At PRG level standard hb_socket*()
functions can be used.
The following things has been changed in PRG hb_socket*() functions:
hb_socketErorrString() can accept <pSocket> as 1-st or 2-nd parameter
and redirect call to socket filter errorStr() method.
hb_socketClose() executes automatically shutdown() for connected
sockets - it is important in windows only where without explicit
call to shutdown() before close transmitted data can be lost.
hb_socketSend() and hb_socketRecv() can be redirected to filter
streams if filter set such redirection. If filter does not redirect
them then they operate on raw sockets. If hb_socketSend() is
redirected then sent data is flushed automatically.
The following new PRG functions has been added:
Add/replace socket filter:
hb_socketSetFilter( <pSocket>, [<cFilterName>], [<hParams>] )
-> <pSocket> | NIL
<cFilterName> is filter name, It's possible to set many filters
in single hb_socketSetFilter() call separating filter names
with "|" character, i.e.:
pSock := hb_socketSetFilter( pSock, "ZSOCK|BFSOCK", hParams )
<hParams> is hash array with initialization parameters used by
given socket filter. The core implementation recognize the
following settings:
"readahead" - numeric value with size of read ahead buffer
"flush" - numeric value with auto flush parameter (for more
information look at hb_socketAutoFlush() below)
"redir" - logical value which can be use to enable/disable
hb_socketSend() and hb_socketRecv() redirection
to filter stream.
Return filter name used by socket:
hb_socketGetFilter( <pSocket> ) -> <cFilterName>
Read from socket stream:
hb_socketRead( <pSocket>, @<cData>, [<nLen> = Len( cData )],
[<nTimeout> = FOREVER] ) -> <nRead>
this function is similar to hb_socketRecv() but is always
redirected to socket stream filters.
Write to socket stream:
hb_socketWrite( <pSocket>, <cData>, [<nLen> = Len( cData )],
[<nTimeout> = FOREVER] ) -> <nWritten>
this function is similar to hb_socketSend() but it is always
redirected to socket stream filters. Written data is not flushed
by default and it should be flushed explicitly by hb_socketFlush().
Automatic flushing can be enabled by hb_socketAutoFlush() function.
Flush data written to socket:
hb_socketFlush( <pSocket>, [<nTimeout> = FOREVER], [<lSync>] )
-> <nNotFlushed>
<lSync> parameter is logical value which can be used to force
special synchronization method in some filters. Usually users
do not have to use it in normal code.
Enable/disable automatic flushing of written data.
hb_socketAutoFlush( <pSocket>, [ <nTimeout> ] ) -> <nTimeout>
<nTimeout> is timeout for automatic flush operation on written
data in milliseconds. <nTimeout> = -1 means wait forever and
<nTimeout> = 0 disables auto flush.
automatic flushing can help in adopting existing code anyhow it
may strongly reduce the performance in some filters, i.e.
compression filters like ZSOCK have to add special data to the
stream after each flush operation so it's suggested to call
flush explicitly when we want to force delivering written data
to the peer.
* include/hbznet.h
* src/rtl/hbznet.c
+ added ZNET socket filter - compressed and encrypted streams are
compatible with hb_znet*() streams. The old hb_znet*() interface
is obsolete for pure socket communication and if not used as
hb_inet*() filter then should be replaced by hb_sockex*() in
user programs.
ZNET socket filter can be created by new PRG functions:
hb_socketNewZNet( <pSocket>, [<cPass>], [<nCompressionLevel>], ;
[<nStrategy>] ) -> <pSocket> | NIL
or by standard socket API with "ZNET" as filter name.
ZNET filter recognize the following settings in initialization
hash array:
"key" or "pass" - string with encryption password
"zlib" - numeric compression level (HB_ZLIB_COMPRESSION_*)
"zs" - numeric ZLIB compression strategy (HB_ZLIB_STRATEGY_*)
ZNET filter always disables any other filters and operates on raw
socket.
Please remember that it's optional module. If programmer does not
use hb_socketNewZNet() explicitly and prefers using hb_socketNew()
then he should force linking this module by REQUEST hb_socketNewZNet
+ added fSync parameter to hb_znetFlush()
[INCOMPATIBLE]
* src/rtl/hbinet.c
* call flush filter function before socket is closed
* src/rtl/Makefile
+ src/rtl/hbzsock.c
+ added ZSOCK socket filter - ZLIB and GZIP compression for socket
streams.
ZSOCK socket filter can be created by new PRG functions:
hb_socketNewZSock( <pSocket>, [<hParams>] ) -> <pSocket> | NIL
or by standard socket API with "ZSOCK" as filter name.
Programmers using hb_socketNew() can force linking this module by
REQUEST hb_socketNewZSock
ZSOCK filter can be used with other filters.
ZSOCK filter recognize the following settings in initialization
hash array:
"zlib" - numeric compression level (HB_ZLIB_COMPRESSION_*)
"zs" - numeric ZLIB compression strategy (HB_ZLIB_STRATEGY_*)
"zin" - logical value which allow to enable/disable ZLIB
decompression on input stream (default)
"gzin" - logical value which allow to enable/disable GZIP
decompression on input stream - it's possible to
enable both ZLIB and GZIP decompression together
so both streams can be decompress
"zout" - logical value which allow to enable/disable ZLIB
compression on output stream (default)
"gzout" - logical value which allow to enable/disable GZIP
compression on output stream - if both "zout" and
"gzout" are enabled GZIP compression is used.
* src/rtl/Makefile
+ src/rtl/hbbfsock.c
+ added BFSOCK socket filter - BlowFish input and output stream
encryption in CTR mode.
BFSOCK socket filter can be created by new PRG functions:
hb_socketNewBFSock( <pSocket>, [<hParams>] ) -> <pSocket> | NIL
or by standard socket API with "BFSOCK" as filter name.
Programmers using hb_socketNew() can force linking this module by
REQUEST hb_socketNewBFSock
BFSOCK filter can be used with other filters, i.e. with ZSOCK.
Please only remember that good encryption algorithms have to
generate data which cannot be compressed so using "BFSOCK|ZSOCK"
only wastes resources and correct filter order is "ZSOCK|BFSOCK".
BFSOCK filter recognize the following settings in initialization
hash array:
"key" or "pass" - string with encryption password
"iv" - string with initialization vector for CTR mode
* contrib/hbssl/hbssl.ch
* contrib/hbssl/hbssl.h
* contrib/hbssl/hbssl.hbm
* contrib/hbssl/hbssl.hbx
* contrib/hbssl/ssl.c
* contrib/hbssl/ssl_inet.c
+ contrib/hbssl/ssl_sock.c
+ added SSL socket filter
SSL socket filter can be created by new PRG functions:
hb_socketNewSSL_connect( <pSocket>, <pSSL> [, <nTimeout> ] )
-> <pSocketSSL> | NIL
hb_socketNewSSL_accept( <pSocket>, <pSSL> [, <nTimeout> ] )
-> <pSocketSSL> | NIL
or by standard socket API with "SSL" as filter name.
Programmers using hb_socketNew() can force linking this module by
REQUEST hb_socketNewSSL_connect
or
REQUEST hb_socketNewSSL_accept
SSL filter always disables any other filters and operates on raw
socket.
SSL filter recognize the following settings in initialization hash
array:
"ctx" or "key" - pointer SSL item <pSSL>
"timeout" - timeout (numeric)
"client" - logical value indicating client mode (SSL_connect())
"server" - logical value indicating server mode (SSL_accept())
* contrib/hbssl/tests/inetssl.prg
! cleaned typo in local function name
* contrib/hbnetio/netiocli.c
* contrib/hbnetio/netiosrv.c
* use new Harbour extended socket API (hb_sockex*()) instead of
raw sockets and hb_znet*()
* contrib/hbtcpio/tcpio.c
* use new Harbour extended socket API (hb_sockex*()) instead of
raw sockets
+ implemented hb_fileFlush()
597 lines
17 KiB
C
597 lines
17 KiB
C
/*
|
|
* Harbour Project source code:
|
|
* Harbour extended socket filter with SSL encryption
|
|
*
|
|
* Copyright 2015 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
|
|
* www - http://harbour-project.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this software; see the file COPYING.txt. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
|
|
*
|
|
* As a special exception, the Harbour Project gives permission for
|
|
* additional uses of the text contained in its release of Harbour.
|
|
*
|
|
* The exception is that, if you link the Harbour libraries with other
|
|
* files to produce an executable, this does not by itself cause the
|
|
* resulting executable to be covered by the GNU General Public License.
|
|
* Your use of that executable is in no way restricted on account of
|
|
* linking the Harbour library code into it.
|
|
*
|
|
* This exception does not however invalidate any other reasons why
|
|
* the executable file might be covered by the GNU General Public License.
|
|
*
|
|
* This exception applies only to the code released by the Harbour
|
|
* Project under the name Harbour. If you copy code from other
|
|
* Harbour Project or Free Software Foundation releases into a copy of
|
|
* Harbour, as the General Public License permits, the exception does
|
|
* not apply to the code that you add in this way. To avoid misleading
|
|
* anyone as to the status of such modified files, you must delete
|
|
* this exception notice from them.
|
|
*
|
|
* If you write modifications of your own for Harbour, it is your choice
|
|
* whether to permit this exception to apply to your modifications.
|
|
* If you do not wish that, delete this exception notice.
|
|
*
|
|
*/
|
|
|
|
/* this has to be declared before hbsocket.h is included */
|
|
#define _HB_SOCKEX_IMPLEMENTATION_
|
|
|
|
#include "hbapiitm.h"
|
|
#include "hbapierr.h"
|
|
#include "hbvm.h"
|
|
#include "hbsocket.h"
|
|
#include "hbdate.h"
|
|
#include "hbssl.h"
|
|
#include "hbinit.h"
|
|
|
|
typedef struct _HB_SSLSTREAM
|
|
{
|
|
SSL * ssl;
|
|
PHB_ITEM pSSL;
|
|
HB_BOOL blocking;
|
|
}
|
|
HB_SSLSTREAM;
|
|
|
|
const char * hb_ssl_socketErrorStr( int iError )
|
|
{
|
|
if( iError >= HB_SSL_SOCK_ERROR_BASE )
|
|
{
|
|
switch( iError - HB_SSL_SOCK_ERROR_BASE )
|
|
{
|
|
case SSL_ERROR_NONE:
|
|
return "SSL_ERROR_NONE";
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
return "SSL_ERROR_ZERO_RETURN";
|
|
case SSL_ERROR_WANT_READ:
|
|
return "SSL_ERROR_WANT_READ";
|
|
case SSL_ERROR_WANT_WRITE:
|
|
return "SSL_ERROR_WANT_WRITE";
|
|
case SSL_ERROR_WANT_CONNECT:
|
|
return "SSL_ERROR_WANT_CONNECT";
|
|
case SSL_ERROR_WANT_ACCEPT:
|
|
return "SSL_ERROR_WANT_ACCEPT";
|
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
return "SSL_ERROR_WANT_X509_LOOKUP";
|
|
case SSL_ERROR_SYSCALL:
|
|
return "SSL_ERROR_SYSCALL";
|
|
case SSL_ERROR_SSL:
|
|
return "SSL_ERROR_SSL";
|
|
}
|
|
}
|
|
|
|
return hb_socketErrorStr( iError );
|
|
}
|
|
|
|
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 > 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;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
iError = 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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
void hb_ssl_socketClose( PHB_SSLSTREAM pStream )
|
|
{
|
|
SSL_shutdown( pStream->ssl );
|
|
hb_itemRelease( pStream->pSSL );
|
|
hb_xfree( pStream );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* socket filter */
|
|
|
|
#define HB_SSLSOCK_GET( p ) ( ( PHB_SSLSTREAM ) p->cargo )
|
|
#define HB_SSLSOCK_READAHEAD 0x40
|
|
|
|
static PHB_SOCKEX s_sockexNew( HB_SOCKET sd, PHB_ITEM pParams )
|
|
{
|
|
PHB_SOCKEX pSock;
|
|
HB_BOOL fServer = HB_FALSE;
|
|
HB_MAXINT timeout = -1;
|
|
SSL * ssl = NULL;
|
|
|
|
if( pParams && HB_IS_HASH( pParams ) )
|
|
{
|
|
PHB_ITEM pItem;
|
|
|
|
if( ssl == NULL && ( pItem = hb_hashGetCItemPtr( pParams, "ctx" ) ) != NULL )
|
|
ssl = hb_SSL_itemGet( pItem );
|
|
if( ssl == NULL && ( pItem = hb_hashGetCItemPtr( pParams, "key" ) ) != NULL )
|
|
ssl = hb_SSL_itemGet( pItem );
|
|
if( ( pItem = hb_hashGetCItemPtr( pParams, "timeout" ) ) != NULL &&
|
|
HB_IS_NUMERIC( pItem ) )
|
|
timeout = hb_itemGetNInt( pItem );
|
|
if( ( pItem = hb_hashGetCItemPtr( pParams, "server" ) ) != NULL &&
|
|
HB_IS_LOGICAL( pItem ) )
|
|
fServer = hb_itemGetL( pItem );
|
|
else if( ( pItem = hb_hashGetCItemPtr( pParams, "client" ) ) != NULL &&
|
|
HB_IS_LOGICAL( pItem ) )
|
|
fServer = ! hb_itemGetL( pItem );
|
|
}
|
|
|
|
pSock = hb_sockexNewSSL( sd, ssl, fServer, timeout );
|
|
if( pSock )
|
|
hb_socekxParamsInit( pSock, pParams );
|
|
|
|
return pSock;
|
|
}
|
|
|
|
/* this wrapper does not support multilevel filtering so
|
|
it destroys previous wrappers if any and create new one.
|
|
*/
|
|
static PHB_SOCKEX s_sockexNext( PHB_SOCKEX pSock, PHB_ITEM pParams )
|
|
{
|
|
PHB_SOCKEX pSockNew = NULL;
|
|
|
|
if( pSock && pSock->sd != HB_NO_SOCKET )
|
|
{
|
|
pSockNew = s_sockexNew( pSock->sd, pParams );
|
|
if( pSockNew )
|
|
hb_sockexClose( pSock, HB_FALSE );
|
|
}
|
|
|
|
return pSockNew;
|
|
}
|
|
|
|
static int s_sockexClose( PHB_SOCKEX pSock, HB_BOOL fClose )
|
|
{
|
|
int iResult;
|
|
|
|
if( pSock->cargo )
|
|
{
|
|
SSL_shutdown( HB_SSLSOCK_GET( pSock )->ssl );
|
|
hb_itemRelease( HB_SSLSOCK_GET( pSock )->pSSL );
|
|
hb_xfree( pSock->cargo );
|
|
}
|
|
|
|
iResult = hb_sockexRawClear( pSock, fClose );
|
|
hb_xfree( pSock );
|
|
|
|
return iResult;
|
|
}
|
|
|
|
static long s_sockexRead( PHB_SOCKEX pSock, void * data, long len, HB_MAXINT timeout )
|
|
{
|
|
long lRead = HB_MIN( pSock->inbuffer, len );
|
|
|
|
if( lRead > 0 )
|
|
{
|
|
memcpy( data, pSock->buffer + pSock->posbuffer, lRead );
|
|
pSock->inbuffer -= lRead;
|
|
if( pSock->inbuffer )
|
|
pSock->posbuffer += lRead;
|
|
else
|
|
pSock->posbuffer = 0;
|
|
return lRead;
|
|
}
|
|
else if( pSock->sd == HB_NO_SOCKET )
|
|
{
|
|
hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE );
|
|
return -1;
|
|
}
|
|
return hb_ssl_socketRead( HB_SSLSOCK_GET( pSock ), pSock->sd, data, len, timeout );
|
|
}
|
|
|
|
static long s_sockexWrite( PHB_SOCKEX pSock, const void * data, long len, HB_MAXINT timeout )
|
|
{
|
|
if( pSock->sd == HB_NO_SOCKET )
|
|
{
|
|
hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE );
|
|
return -1;
|
|
}
|
|
return hb_ssl_socketWrite( HB_SSLSOCK_GET( pSock ), pSock->sd, data, len, timeout, NULL );
|
|
}
|
|
|
|
static long s_sockexFlush( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync )
|
|
{
|
|
HB_SYMBOL_UNUSED( pSock );
|
|
HB_SYMBOL_UNUSED( timeout );
|
|
HB_SYMBOL_UNUSED( fSync );
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
static int s_sockexCanWrite( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout )
|
|
{
|
|
if( pSock->sd == HB_NO_SOCKET )
|
|
{
|
|
hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE );
|
|
return -1;
|
|
}
|
|
return fBuffer ? 0 : hb_socketSelectWrite( pSock->sd, timeout );
|
|
}
|
|
|
|
static char * s_sockexName( PHB_SOCKEX pSock )
|
|
{
|
|
return hb_strdup( pSock->pFilter->pszName );
|
|
}
|
|
|
|
static const char * s_sockexErrorStr( PHB_SOCKEX pSock, int iError )
|
|
{
|
|
HB_SYMBOL_UNUSED( pSock );
|
|
|
|
return hb_ssl_socketErrorStr( iError );
|
|
}
|
|
|
|
static const HB_SOCKET_FILTER s_sockFilter =
|
|
{
|
|
"SSL",
|
|
s_sockexNew,
|
|
s_sockexNext,
|
|
s_sockexClose,
|
|
s_sockexRead,
|
|
s_sockexWrite,
|
|
s_sockexFlush,
|
|
s_sockexCanRead,
|
|
s_sockexCanWrite,
|
|
s_sockexName,
|
|
s_sockexErrorStr
|
|
};
|
|
|
|
PHB_SOCKEX hb_sockexNewSSL( HB_SOCKET sd, SSL * ssl, HB_BOOL fServer,
|
|
HB_MAXINT timeout )
|
|
{
|
|
PHB_SOCKEX pSock = NULL;
|
|
|
|
if( sd != HB_NO_SOCKET && ssl )
|
|
{
|
|
PHB_SSLSTREAM pStream = hb_ssl_socketNew( sd, ssl, fServer, timeout, NULL );
|
|
if( pStream )
|
|
{
|
|
pSock = ( PHB_SOCKEX ) hb_xgrabz( sizeof( HB_SOCKEX ) );
|
|
pSock->sd = sd;
|
|
pSock->fRedirAll = HB_TRUE;
|
|
pSock->fShutDown = HB_TRUE;
|
|
pSock->pFilter = &s_sockFilter;
|
|
pSock->cargo = ( void * ) pStream;
|
|
}
|
|
}
|
|
|
|
return pSock;
|
|
}
|
|
|
|
static void s_sslSocketNew( HB_BOOL fServer )
|
|
{
|
|
HB_SOCKET sd = hb_socketParam( 1 );
|
|
|
|
if( sd != HB_NO_SOCKET )
|
|
{
|
|
PHB_SOCKEX pSock = NULL;
|
|
SSL * ssl = hb_SSL_par( 2 );
|
|
|
|
if( ssl )
|
|
pSock = hb_sockexNewSSL( sd, ssl, fServer, hb_parnintdef( 3, - 1 ) );
|
|
else if( HB_ISHASH( 2 ) )
|
|
pSock = hb_sockexNew( sd, s_sockFilter.pszName, hb_param( 2, HB_IT_ANY ) );
|
|
else
|
|
hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
|
|
|
|
if( pSock )
|
|
{
|
|
hb_socketItemClear( hb_param( 1, HB_IT_POINTER ) );
|
|
hb_sockexItemPut( hb_param( -1, HB_IT_ANY ), pSock );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* hb_socketNewSSL_connect( <pSocket>, <pSSL> [, <nTimeout> ] ) */
|
|
HB_FUNC( HB_SOCKETNEWSSL_CONNECT )
|
|
{
|
|
s_sslSocketNew( HB_FALSE );
|
|
}
|
|
|
|
/* hb_socketNewSSL_accept( <pSocket>, <pSSL> [, <nTimeout> ] ) */
|
|
HB_FUNC( HB_SOCKETNEWSSL_ACCEPT )
|
|
{
|
|
s_sslSocketNew( HB_TRUE );
|
|
}
|
|
|
|
HB_CALL_ON_STARTUP_BEGIN( _hb_sslsock_init_ )
|
|
hb_sockexRegister( &s_sockFilter );
|
|
HB_CALL_ON_STARTUP_END( _hb_sslsock_init_ )
|
|
|
|
#if defined( HB_PRAGMA_STARTUP )
|
|
#pragma startup _hb_sslsock_init_
|
|
#elif defined( HB_DATASEG_STARTUP )
|
|
#define HB_DATASEG_BODY HB_DATASEG_FUNC( _hb_sslsock_init_ )
|
|
#include "hbiniseg.h"
|
|
#endif
|