From 41b8ecb6c79b21abd7593ea0785c0960a7b20549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Czerpak?= Date: Wed, 26 Aug 2015 15:51:35 +0200 Subject: [PATCH] 2015-08-26 15:51 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) * 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 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( , [], [] ) -> | NIL 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 ) 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( ) -> Read from socket stream: hb_socketRead( , @, [ = Len( cData )], [ = FOREVER] ) -> this function is similar to hb_socketRecv() but is always redirected to socket stream filters. Write to socket stream: hb_socketWrite( , , [ = Len( cData )], [ = FOREVER] ) -> 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( , [ = FOREVER], [] ) -> 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( , [ ] ) -> is timeout for automatic flush operation on written data in milliseconds. = -1 means wait forever and = 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( , [], [], ; [] ) -> | 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( , [] ) -> | 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( , [] ) -> | 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( , [, ] ) -> | NIL hb_socketNewSSL_accept( , [, ] ) -> | 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 "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() --- ChangeLog.txt | 178 +++++++ contrib/hbnetio/netiocli.c | 117 ++-- contrib/hbnetio/netiosrv.c | 112 ++-- contrib/hbssl/hbssl.ch | 2 + contrib/hbssl/hbssl.h | 17 + contrib/hbssl/hbssl.hbm | 1 + contrib/hbssl/hbssl.hbx | 2 + contrib/hbssl/ssl.c | 7 + contrib/hbssl/ssl_inet.c | 307 +---------- contrib/hbssl/ssl_sock.c | 596 +++++++++++++++++++++ contrib/hbssl/tests/inetssl.prg | 4 +- contrib/hbtcpio/tcpio.c | 48 +- include/harbour.hbx | 9 + include/hbsocket.h | 78 +++ include/hbznet.h | 9 +- src/rtl/Makefile | 2 + src/rtl/hbbfsock.c | 370 +++++++++++++ src/rtl/hbinet.c | 7 +- src/rtl/hbsockhb.c | 913 +++++++++++++++++++++++++++++--- src/rtl/hbznet.c | 315 ++++++++++- src/rtl/hbzsock.c | 575 ++++++++++++++++++++ 21 files changed, 3148 insertions(+), 521 deletions(-) create mode 100644 contrib/hbssl/ssl_sock.c create mode 100644 src/rtl/hbbfsock.c create mode 100644 src/rtl/hbzsock.c diff --git a/ChangeLog.txt b/ChangeLog.txt index 0e9d0b3c49..f42a52f6a3 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -10,6 +10,184 @@ * Change, ! Fix, % Optimization, + Addition, - Removal, ; Comment */ +2015-08-26 15:51 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) + * 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 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( , [], [] ) + -> | NIL + 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 ) + 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( ) -> + Read from socket stream: + hb_socketRead( , @, [ = Len( cData )], + [ = FOREVER] ) -> + this function is similar to hb_socketRecv() but is always + redirected to socket stream filters. + Write to socket stream: + hb_socketWrite( , , [ = Len( cData )], + [ = FOREVER] ) -> + 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( , [ = FOREVER], [] ) + -> + 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( , [ ] ) -> + is timeout for automatic flush operation on written + data in milliseconds. = -1 means wait forever and + = 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( , [], [], ; + [] ) -> | 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( , [] ) -> | 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( , [] ) -> | 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( , [, ] ) + -> | NIL + hb_socketNewSSL_accept( , [, ] ) + -> | 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 + "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() + 2015-08-17 15:39 UTC+0200 Przemyslaw Czerpak (druzus/at/poczta.onet.pl) * include/hbznet.h + include hbsocket.h diff --git a/contrib/hbnetio/netiocli.c b/contrib/hbnetio/netiocli.c index 8b519d98c7..441952b5c8 100644 --- a/contrib/hbnetio/netiocli.c +++ b/contrib/hbnetio/netiocli.c @@ -115,8 +115,7 @@ typedef struct _HB_CONCLI HB_ERRCODE errcode; int timeout; int port; - HB_SOCKET sd; - PHB_ZNETSTREAM zstream; + PHB_SOCKEX sock; PHB_SRVDATA srvdata; struct _HB_CONCLI * next; int level; @@ -189,10 +188,7 @@ static long s_fileRecvAll( PHB_CONCLI conn, void * buffer, long len ) while( lRead < len ) { - if( conn->zstream ) - l = hb_znetRead( conn->zstream, conn->sd, ptr + lRead, len - lRead, conn->timeout ); - else - l = hb_socketRecv( conn->sd, ptr + lRead, len - lRead, 0, conn->timeout ); + l = hb_sockexRead( conn->sock, ptr + lRead, len - lRead, conn->timeout ); if( l <= 0 ) break; lRead += l; @@ -208,10 +204,7 @@ static long s_fileRecvTest( PHB_CONCLI conn, void * buffer, long len ) while( lRead < len ) { - if( conn->zstream ) - l = hb_znetRead( conn->zstream, conn->sd, ptr + lRead, len - lRead, timeout ); - else - l = hb_socketRecv( conn->sd, ptr + lRead, len - lRead, 0, timeout ); + l = hb_sockexRead( conn->sock, ptr + lRead, len - lRead, timeout ); if( l <= 0 ) break; lRead += l; @@ -371,7 +364,7 @@ static HB_BOOL s_fileSendMsg( PHB_CONCLI conn, HB_BYTE * msgbuf, { HB_BYTE buffer[ 2048 ]; HB_BYTE * msg, * ptr = NULL; - HB_LONG lSent = 0, lLast = 1, l; + HB_LONG lSent = 0, l; HB_BOOL fResult = HB_FALSE; if( len == 0 ) @@ -392,14 +385,10 @@ static HB_BOOL s_fileSendMsg( PHB_CONCLI conn, HB_BYTE * msgbuf, while( lSent < len ) { - if( conn->zstream ) - l = hb_znetWrite( conn->zstream, conn->sd, msg + lSent, len - lSent, conn->timeout, &lLast ); - else - l = lLast = hb_socketSend( conn->sd, msg + lSent, len - lSent, 0, conn->timeout ); - if( l > 0 ) - lSent += l; - if( lLast <= 0 ) + l = hb_sockexWrite( conn->sock, msg + lSent, len - lSent, conn->timeout ); + if( l <= 0 ) break; + lSent += l; } if( ptr ) @@ -407,8 +396,7 @@ static HB_BOOL s_fileSendMsg( PHB_CONCLI conn, HB_BYTE * msgbuf, if( lSent == len ) { - if( conn->zstream && - hb_znetFlush( conn->zstream, conn->sd, conn->timeout ) != 0 ) + if( hb_sockexFlush( conn->sock, conn->timeout, HB_FALSE ) != 0 ) { conn->errcode = hb_socketGetError(); if( ! fNoError ) @@ -527,6 +515,24 @@ static HB_BOOL s_fileProcessData( PHB_CONCLI conn ) return fResult; } +static void s_fileConFree( PHB_CONCLI conn ) +{ + hb_sockexClose( conn->sock, HB_TRUE ); + while( conn->srvdata ) + { + PHB_SRVDATA pSrvData = conn->srvdata; + conn->srvdata = pSrvData->next; + if( pSrvData->array ) + hb_itemRelease( pSrvData->array ); + if( pSrvData->data ) + hb_xfree( pSrvData->data ); + hb_xfree( pSrvData ); + } + if( conn->mutex ) + hb_itemRelease( conn->mutex ); + hb_xfree( conn ); +} + static PHB_CONCLI s_fileConNew( HB_SOCKET sd, const char * pszServer, int iPort, int iTimeOut, const char * pszPasswd, int iPassLen, @@ -541,8 +547,6 @@ static PHB_CONCLI s_fileConNew( HB_SOCKET sd, const char * pszServer, hb_atomic_set( &conn->usrcount, 0 ); conn->mutex = hb_threadMutexCreate(); conn->errcode = 0; - conn->sd = sd; - conn->zstream = NULL; conn->srvdata = NULL; conn->next = NULL; conn->timeout = iTimeOut; @@ -554,28 +558,21 @@ static PHB_CONCLI s_fileConNew( HB_SOCKET sd, const char * pszServer, if( iPassLen ) memcpy( conn->passwd, pszPasswd, iPassLen ); - return conn; -} + if( iLevel == HB_ZLIB_COMPRESSION_DISABLE ) + conn->sock = hb_sockexNew( sd, NULL, NULL ); + else + conn->sock = hb_sockexNewZNet( sd, pszPasswd, iPassLen, + iLevel, iStrategy ); -static void s_fileConFree( PHB_CONCLI conn ) -{ - hb_socketShutdown( conn->sd, HB_SOCKET_SHUT_RDWR ); - hb_socketClose( conn->sd ); - while( conn->srvdata ) + if( conn->sock == NULL ) { - PHB_SRVDATA pSrvData = conn->srvdata; - conn->srvdata = pSrvData->next; - if( pSrvData->array ) - hb_itemRelease( pSrvData->array ); - if( pSrvData->data ) - hb_xfree( pSrvData->data ); - hb_xfree( pSrvData ); + s_fileConFree( conn ); + conn = NULL; } - if( conn->zstream ) - hb_znetClose( conn->zstream ); - if( conn->mutex ) - hb_itemRelease( conn->mutex ); - hb_xfree( conn ); + else + hb_sockexSetShutDown( conn->sock, HB_TRUE ); + + return conn; } static void s_fileConRegister( PHB_CONCLI conn ) @@ -842,7 +839,6 @@ static PHB_CONCLI s_fileConnect( const char ** pFileName, int iLevel, int iStrategy ) { PHB_CONCLI conn; - HB_SOCKET sd; char server[ NETIO_SERVERNAME_MAX ]; char * pszIpAddres; @@ -863,7 +859,7 @@ static PHB_CONCLI s_fileConnect( const char ** pFileName, conn = s_fileConFind( pszIpAddres, iPort ); if( conn == NULL ) { - sd = hb_socketOpen( HB_SOCKET_PF_INET, HB_SOCKET_PT_STREAM, 0 ); + HB_SOCKET sd = hb_socketOpen( HB_SOCKET_PF_INET, HB_SOCKET_PT_STREAM, 0 ); if( sd != HB_NO_SOCKET ) { void * pSockAddr; @@ -874,35 +870,18 @@ static PHB_CONCLI s_fileConnect( const char ** pFileName, hb_socketSetKeepAlive( sd, HB_TRUE ); if( hb_socketConnect( sd, pSockAddr, uiLen, iTimeOut ) == 0 ) { - HB_BYTE msgbuf[ NETIO_MSGLEN ]; - HB_U16 len = ( HB_U16 ) strlen( NETIO_LOGINSTRID ); - - HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_LOGIN ); - HB_PUT_LE_UINT16( &msgbuf[ 4 ], len ); - memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); - hb_socketSetNoDelay( sd, HB_TRUE ); conn = s_fileConNew( sd, pszIpAddres, iPort, iTimeOut, pszPasswd, iPassLen, iLevel, iStrategy ); - sd = HB_NO_SOCKET; - - if( iLevel != HB_ZLIB_COMPRESSION_DISABLE ) - { - conn->zstream = hb_znetOpen( iLevel, iStrategy ); - if( conn->zstream != NULL ) - { - if( iPassLen ) - hb_znetEncryptKey( conn->zstream, pszPasswd, iPassLen ); - } - else - { - s_fileConFree( conn ); - conn = NULL; - } - } - if( conn ) { + HB_BYTE msgbuf[ NETIO_MSGLEN ]; + HB_U16 len = ( HB_U16 ) strlen( NETIO_LOGINSTRID ); + + HB_PUT_LE_UINT32( &msgbuf[ 0 ], NETIO_LOGIN ); + HB_PUT_LE_UINT16( &msgbuf[ 4 ], len ); + memset( msgbuf + 6, '\0', sizeof( msgbuf ) - 6 ); + if( ! s_fileSendMsg( conn, msgbuf, NETIO_LOGINSTRID, len, HB_TRUE, fNoError ) || HB_GET_LE_UINT32( &msgbuf[ 4 ] ) != NETIO_CONNECTED ) @@ -912,6 +891,8 @@ static PHB_CONCLI s_fileConnect( const char ** pFileName, } else s_fileConRegister( conn ); + + sd = HB_NO_SOCKET; } } hb_xfree( pSockAddr ); @@ -2517,7 +2498,7 @@ static HB_BOOL s_fileConfigure( PHB_FILE pFile, int iIndex, PHB_ITEM pValue ) static HB_FHANDLE s_fileHandle( PHB_FILE pFile ) { - return pFile ? pFile->conn->sd : HB_NO_SOCKET; + return pFile ? hb_sockexGetHandle( pFile->conn->sock ) : HB_NO_SOCKET; } static const HB_FILE_FUNCS * s_fileMethods( void ) diff --git a/contrib/hbnetio/netiosrv.c b/contrib/hbnetio/netiosrv.c index 1d88f18ed7..0106abbe96 100644 --- a/contrib/hbnetio/netiosrv.c +++ b/contrib/hbnetio/netiosrv.c @@ -101,8 +101,7 @@ HB_CONSTREAM, * PHB_CONSTREAM; typedef struct _HB_CONSRV { - HB_SOCKET sd; - PHB_ZNETSTREAM zstream; + PHB_SOCKEX sock; PHB_FILE fileTable[ NETIO_FILES_MAX ]; int filesCount; int firstFree; @@ -236,17 +235,10 @@ static PHB_FILE s_srvFileGet( PHB_CONSRV conn, int iFile ) static void s_consrv_disconnect( PHB_CONSRV conn ) { - if( conn->sd != HB_NO_SOCKET ) + if( conn->sock ) { - hb_socketShutdown( conn->sd, HB_SOCKET_SHUT_RDWR ); - hb_socketClose( conn->sd ); - conn->sd = HB_NO_SOCKET; - } - - if( conn->zstream ) - { - hb_znetClose( conn->zstream ); - conn->zstream = NULL; + hb_sockexClose( conn->sock, HB_TRUE ); + conn->sock = NULL; } } @@ -267,14 +259,8 @@ static void s_consrv_close( PHB_CONSRV conn ) if( conn->mutex ) hb_itemRelease( conn->mutex ); - if( conn->sd != HB_NO_SOCKET ) - { - hb_socketShutdown( conn->sd, HB_SOCKET_SHUT_RDWR ); - hb_socketClose( conn->sd ); - } - - if( conn->zstream ) - hb_znetClose( conn->zstream ); + if( conn->sock ) + hb_sockexClose( conn->sock, HB_TRUE ); while( conn->filesCount > 0 ) { @@ -343,12 +329,12 @@ static void s_consrvRet( PHB_CONSRV conn ) hb_ret(); } -static PHB_CONSRV s_consrvNew( HB_SOCKET connsd, const char * szRootPath, HB_BOOL rpc ) +static PHB_CONSRV s_consrvNew( PHB_SOCKEX sock, const char * szRootPath, HB_BOOL rpc ) { PHB_CONSRV conn = ( PHB_CONSRV ) memset( hb_xgrab( sizeof( HB_CONSRV ) ), 0, sizeof( HB_CONSRV ) ); - conn->sd = connsd; + conn->sock = sock; conn->rpc = rpc; conn->timeout = -1; if( szRootPath ) @@ -370,10 +356,7 @@ static HB_BOOL s_srvRecvAll( PHB_CONSRV conn, void * buffer, long len ) while( lRead < len && ! conn->stop ) { - if( conn->zstream ) - l = hb_znetRead( conn->zstream, conn->sd, ptr + lRead, len - lRead, 1000 ); - else - l = hb_socketRecv( conn->sd, ptr + lRead, len - lRead, 0, 1000 ); + l = hb_sockexRead( conn->sock, ptr + lRead, len - lRead, 1000 ); if( l <= 0 ) { if( l == 0 || @@ -395,7 +378,7 @@ static HB_BOOL s_srvRecvAll( PHB_CONSRV conn, void * buffer, long len ) static HB_BOOL s_srvSendAll( PHB_CONSRV conn, void * buffer, long len ) { HB_BYTE * ptr = ( HB_BYTE * ) buffer; - long lSent = 0, lLast = 1, l; + long lSent = 0, l; HB_MAXUINT end_timer; if( ! conn->mutex || hb_threadMutexLock( conn->mutex ) ) @@ -404,16 +387,13 @@ static HB_BOOL s_srvSendAll( PHB_CONSRV conn, void * buffer, long len ) while( lSent < len && ! conn->stop ) { - if( conn->zstream ) - l = hb_znetWrite( conn->zstream, conn->sd, ptr + lSent, len - lSent, 1000, &lLast ); - else - l = lLast = hb_socketSend( conn->sd, ptr + lSent, len - lSent, 0, 1000 ); + l = hb_sockexWrite( conn->sock, ptr + lSent, len - lSent, 1000 ); if( l > 0 ) { lSent += l; conn->wr_count += l; } - if( lLast <= 0 ) + else { if( hb_socketGetError() != HB_SOCKET_ERR_TIMEOUT || hb_vmRequestQuery() != 0 || @@ -421,10 +401,10 @@ static HB_BOOL s_srvSendAll( PHB_CONSRV conn, void * buffer, long len ) break; } } - if( conn->zstream && lLast > 0 && ! conn->stop ) + if( lSent == len && ! conn->stop ) { - if( hb_znetFlush( conn->zstream, conn->sd, - conn->timeout > 0 ? conn->timeout : -1 ) != 0 ) + if( hb_sockexFlush( conn->sock, conn->timeout > 0 ? conn->timeout : -1, + HB_FALSE ) != 0 ) lSent = -1; } @@ -659,25 +639,20 @@ HB_FUNC( NETIO_ACCEPT ) if( connsd != HB_NO_SOCKET ) { + PHB_SOCKEX sock; + hb_socketSetKeepAlive( connsd, HB_TRUE ); hb_socketSetNoDelay( connsd, HB_TRUE ); - conn = s_consrvNew( connsd, lsd->rootPath, lsd->rpc ); - - if( iLevel != HB_ZLIB_COMPRESSION_DISABLE ) - { - conn->zstream = hb_znetOpen( iLevel, iStrategy ); - if( conn->zstream != NULL ) - { - if( keylen ) - hb_znetEncryptKey( conn->zstream, hb_parc( 3 ), keylen ); - } - else - { - s_consrv_close( conn ); - conn = NULL; - } - } + if( iLevel == HB_ZLIB_COMPRESSION_DISABLE ) + sock = hb_sockexNew( connsd, NULL, NULL ); + else + sock = hb_sockexNewZNet( connsd, hb_parc( 3 ), keylen, + iLevel, iStrategy ); + if( sock != NULL ) + conn = s_consrvNew( sock, lsd->rootPath, lsd->rpc ); + else + hb_socketClose( connsd ); } } @@ -691,9 +666,10 @@ HB_FUNC( NETIO_COMPRESS ) { PHB_CONSRV conn = s_consrvParam( 1 ); - if( conn && conn->sd != HB_NO_SOCKET && ! conn->stop ) + if( conn && conn->sock && ! conn->stop ) { int iLevel, iStrategy, keylen = ( int ) hb_parclen( 2 ); + PHB_SOCKEX sock; if( keylen > NETIO_PASSWD_MAX ) keylen = NETIO_PASSWD_MAX; @@ -702,31 +678,21 @@ HB_FUNC( NETIO_COMPRESS ) iStrategy = hb_parnidef( 4, HB_ZLIB_STRATEGY_DEFAULT ); if( iLevel == HB_ZLIB_COMPRESSION_DISABLE ) - { - if( conn->zstream ) - { - hb_znetClose( conn->zstream ); - conn->zstream = NULL; - } - } + sock = hb_sockexNew( hb_sockexGetHandle( conn->sock ), NULL, NULL ); else + sock = hb_sockexNewZNet( hb_sockexGetHandle( conn->sock ), hb_parc( 2 ), keylen, + iLevel, iStrategy ); + if( sock ) { - PHB_ZNETSTREAM zstream = hb_znetOpen( iLevel, iStrategy ); - if( zstream != NULL ) - { - if( conn->zstream ) - hb_znetClose( conn->zstream ); - conn->zstream = zstream; - if( keylen ) - hb_znetEncryptKey( zstream, hb_parc( 2 ), keylen ); - } + hb_sockexClose( conn->sock, HB_FALSE ); + conn->sock = sock; } } } static HB_BOOL s_netio_login_accept( PHB_CONSRV conn ) { - if( conn && conn->sd != HB_NO_SOCKET && ! conn->stop && ! conn->login ) + if( conn && conn->sock && ! conn->stop && ! conn->login ) { HB_BYTE msgbuf[ NETIO_MSGLEN ]; @@ -1677,7 +1643,7 @@ HB_FUNC( NETIO_SRVSENDITEM ) PHB_ITEM pItem = hb_param( 3, HB_IT_ANY ); HB_BOOL fResult = HB_FALSE; - if( conn && conn->sd != HB_NO_SOCKET && ! conn->stop && conn->mutex && + if( conn && conn->sock && ! conn->stop && conn->mutex && iStreamID && pItem ) { char * itmData, * msg; @@ -1722,7 +1688,7 @@ HB_FUNC( NETIO_SRVSENDDATA ) long lLen = ( long ) hb_parclen( 3 ); HB_BOOL fResult = HB_FALSE; - if( conn && conn->sd != HB_NO_SOCKET && ! conn->stop && conn->mutex && + if( conn && conn->sock && ! conn->stop && conn->mutex && iStreamID && lLen > 0 ) { char * msg; @@ -1770,7 +1736,7 @@ HB_FUNC( NETIO_SRVSTATUS ) if( ! conn ) iStatus = NETIO_SRVSTAT_WRONGHANDLE; - else if( conn->sd == HB_NO_SOCKET ) + else if( conn->sock == NULL ) iStatus = NETIO_SRVSTAT_CLOSED; else if( conn->stop ) iStatus = NETIO_SRVSTAT_STOPPED; @@ -1811,7 +1777,7 @@ HB_FUNC( NETIO_SRVSTATUS ) unsigned int len; PHB_ITEM pItem = NULL; - if( hb_socketGetPeerName( conn->sd, &addr, &len ) == 0 ) + if( hb_socketGetPeerName( hb_sockexGetHandle( conn->sock ), &addr, &len ) == 0 ) { pItem = hb_socketAddrToItem( addr, len ); if( addr ) diff --git a/contrib/hbssl/hbssl.ch b/contrib/hbssl/hbssl.ch index 7dd8ac2722..ff9e84babb 100644 --- a/contrib/hbssl/hbssl.ch +++ b/contrib/hbssl/hbssl.ch @@ -82,6 +82,8 @@ #define HB_SSL_ERROR_WANT_CONNECT 7 #define HB_SSL_ERROR_WANT_ACCEPT 8 +#define HB_SSL_SOCK_ERROR_BASE 100 + #define HB_SSL_OP_MICROSOFT_SESS_ID_BUG 0x00000001 #define HB_SSL_OP_NETSCAPE_CHALLENGE_BUG 0x00000002 #define HB_SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG 0x00000008 diff --git a/contrib/hbssl/hbssl.h b/contrib/hbssl/hbssl.h index 35501ad767..7f4e5110f1 100644 --- a/contrib/hbssl/hbssl.h +++ b/contrib/hbssl/hbssl.h @@ -50,6 +50,7 @@ #define HBSSL_H_ #include "hbapi.h" +#include "hbsocket.h" #if defined( HB_OS_WIN ) #if ! defined( HB_OPENSSL_STATIC ) @@ -167,6 +168,21 @@ HB_EXTERN_BEGIN +struct _HB_SSLSTREAM; +typedef struct _HB_SSLSTREAM * PHB_SSLSTREAM; + +extern PHB_SOCKEX hb_sockexNewSSL( HB_SOCKET sd, SSL * ssl, HB_BOOL fServer, + HB_MAXINT timeout ); +extern PHB_SSLSTREAM hb_ssl_socketNew( HB_SOCKET sd, SSL * ssl, HB_BOOL fServer, + HB_MAXINT timeout, int * piResult ); +extern void hb_ssl_socketClose( PHB_SSLSTREAM pStream ); +extern const char * hb_ssl_socketErrorStr( int iError ); +extern long hb_ssl_socketRead( PHB_SSLSTREAM pStream, HB_SOCKET sd, + void * buffer, long len, HB_MAXINT timeout ); +extern long hb_ssl_socketWrite( PHB_SSLSTREAM pStream, HB_SOCKET sd, + const void * buffer, long len, + HB_MAXINT timeout, long * plast ); + extern const SSL_METHOD * hb_ssl_method_id_to_ptr( int n ); extern void * hb_BIO_is( int iParam ); @@ -177,6 +193,7 @@ extern SSL_CTX * hb_SSL_CTX_par( int iParam ); extern void * hb_SSL_is( int iParam ); extern SSL * hb_SSL_par( int iParam ); +extern SSL * hb_SSL_itemGet( PHB_ITEM pItem ); extern void * hb_SSL_SESSION_is( int iParam ); extern SSL_SESSION * hb_SSL_SESSION_par( int iParam ); diff --git a/contrib/hbssl/hbssl.hbm b/contrib/hbssl/hbssl.hbm index 7ac1d22e56..d98d07d28d 100644 --- a/contrib/hbssl/hbssl.hbm +++ b/contrib/hbssl/hbssl.hbm @@ -40,6 +40,7 @@ hbssl.hbx ssl_hb.c ssl_inet.c +ssl_sock.c bio.c err.c evp.c diff --git a/contrib/hbssl/hbssl.hbx b/contrib/hbssl/hbssl.hbx index eb5be28a7a..75ae9bb2dd 100644 --- a/contrib/hbssl/hbssl.hbx +++ b/contrib/hbssl/hbssl.hbx @@ -174,6 +174,8 @@ DYNAMIC hb_EVP_CIPHER_ctx_create DYNAMIC hb_EVP_ENCODE_ctx_create DYNAMIC hb_inetSSL_accept DYNAMIC hb_inetSSL_connect +DYNAMIC hb_socketNewSSL_accept +DYNAMIC hb_socketNewSSL_connect DYNAMIC hb_SSL_read_all DYNAMIC hb_SSL_read_line DYNAMIC OpenSSL_add_all_algorithms diff --git a/contrib/hbssl/ssl.c b/contrib/hbssl/ssl.c index 454c80c11d..c09e09d5f0 100644 --- a/contrib/hbssl/ssl.c +++ b/contrib/hbssl/ssl.c @@ -145,6 +145,13 @@ SSL * hb_SSL_par( int iParam ) return ph ? ( SSL * ) *ph : NULL; } +SSL * hb_SSL_itemGet( PHB_ITEM pItem ) +{ + void ** ph = ( void ** ) hb_itemGetPtrGC( pItem, &s_gcSSL_funcs ); + + return ph ? ( SSL * ) *ph : NULL; +} + HB_FUNC( SSL_NEW ) { if( hb_SSL_CTX_is( 1 ) ) diff --git a/contrib/hbssl/ssl_inet.c b/contrib/hbssl/ssl_inet.c index 786666e678..e7c9ed2db9 100644 --- a/contrib/hbssl/ssl_inet.c +++ b/contrib/hbssl/ssl_inet.c @@ -56,238 +56,47 @@ #include "hbznet.h" #include "hbssl.h" -#define HB_ZNET_SSL_ERROR_BASE 100 - -typedef struct _HB_ZNETSTREAM -{ - SSL * ssl; - PHB_ITEM pSSL; - int error; - HB_BOOL blocking; -} -HB_ZNETSTREAM; - static long hb_inetReadSSL( PHB_ZNETSTREAM 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; - } - - pStream->error = 0; - - if( len > 0 ) - { - iToRead = SSL_pending( pStream->ssl ); - if( iToRead <= 0 ) - { - iToRead = timeout < 0 ? 1 : hb_socketSelectRead( sd, timeout ); - if( iToRead <= 0 ) - pStream->error = iToRead == 0 ? - HB_SOCKET_ERR_TIMEOUT : hb_socketGetError(); - else - iToRead = ( int ) len; - } - else if( iToRead > len ) - iToRead = ( int ) len; - } - - while( iToRead > 0 ) - { - lRead = SSL_read( pStream->ssl, buffer, iToRead ); - if( lRead > 0 ) - pStream->error = 0; - else - { - int iError = SSL_get_error( pStream->ssl, ( int ) lRead ); - switch( iError ) - { - case SSL_ERROR_ZERO_RETURN: - pStream->error = HB_SOCKET_ERR_PIPE; - lRead = 0; - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - 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 ) - { - pStream->error = hb_socketGetError(); - break; - } - } - } - pStream->error = HB_SOCKET_ERR_TIMEOUT; - break; - default: - pStream->error = HB_ZNET_SSL_ERROR_BASE + iError; - } - } - break; - } - - return lRead; + return hb_ssl_socketRead( ( PHB_SSLSTREAM ) pStream, sd, buffer, len, timeout ); } static long hb_inetWriteSSL( PHB_ZNETSTREAM 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; - pStream->error = 0; - } - else - { - int iError = SSL_get_error( pStream->ssl, ( int ) lWr ); - switch( iError ) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - 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 ) - pStream->error = iError < 0 ? hb_socketGetError() : - HB_SOCKET_ERR_TIMEOUT; - break; - } - } - if( lWritten == 0 ) - pStream->error = HB_SOCKET_ERR_TIMEOUT; - break; - default: - pStream->error = HB_ZNET_SSL_ERROR_BASE + iError; - } - break; - } - } - - return lWritten != 0 ? lWritten : lWr; -} - -static long hb_inetFlushSSL( PHB_ZNETSTREAM pStream, HB_SOCKET sd, - HB_MAXINT timeout ) -{ - HB_SYMBOL_UNUSED( pStream ); - HB_SYMBOL_UNUSED( sd ); - HB_SYMBOL_UNUSED( timeout ); - - return 0; + return hb_ssl_socketWrite( ( PHB_SSLSTREAM ) pStream, sd, buffer, len, timeout, plast ); } static void hb_inetCloseSSL( PHB_ZNETSTREAM pStream ) { - SSL_shutdown( pStream->ssl ); - hb_itemRelease( pStream->pSSL ); - hb_xfree( pStream ); + hb_ssl_socketClose( ( PHB_SSLSTREAM ) pStream ); +} + +static long hb_inetFlushSSL( PHB_ZNETSTREAM pStream, HB_SOCKET sd, + HB_MAXINT timeout, HB_BOOL fSync ) +{ + HB_SYMBOL_UNUSED( pStream ); + HB_SYMBOL_UNUSED( sd ); + HB_SYMBOL_UNUSED( timeout ); + HB_SYMBOL_UNUSED( fSync ); + + return 0; } static int hb_inetErrorSSL( PHB_ZNETSTREAM pStream ) { - return pStream->error; + HB_SYMBOL_UNUSED( pStream ); + + return hb_socketGetError(); } static const char * hb_inetErrStrSSL( PHB_ZNETSTREAM pStream, int iError ) { HB_SYMBOL_UNUSED( pStream ); - if( iError >= HB_ZNET_SSL_ERROR_BASE ) - { - switch( iError - HB_ZNET_SSL_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 ); + return hb_ssl_socketErrorStr( iError ); } static void hb_inetStartSSL( HB_BOOL fServer ) @@ -304,79 +113,21 @@ static void hb_inetStartSSL( HB_BOOL fServer ) if( ssl ) { - PHB_ZNETSTREAM pStream = ( HB_ZNETSTREAM * ) hb_xgrabz( sizeof( HB_ZNETSTREAM ) ); - HB_MAXINT timeout; - HB_MAXUINT timer; - - timeout = HB_ISNUM( 3 ) ? hb_parnint( 3 ) : - hb_znetInetTimeout( pItem, HB_FALSE ); - 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 ) + HB_MAXINT timeout = HB_ISNUM( 3 ) ? hb_parnint( 3 ) : + hb_znetInetTimeout( pItem, HB_FALSE ); + PHB_SSLSTREAM pStream = hb_ssl_socketNew( sd, ssl, fServer, + timeout, &iResult ); + if( pStream ) { - if( fServer ) - iResult = SSL_accept( ssl ); - else - iResult = SSL_connect( ssl ); - - if( iResult != 1 ) + if( ! hb_znetInetInitialize( pItem, ( PHB_ZNETSTREAM ) pStream, + hb_inetReadSSL, hb_inetWriteSSL, + hb_inetFlushSSL, hb_inetCloseSSL, + hb_inetErrorSSL, hb_inetErrStrSSL ) ) { - 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; - } - } - } - iResult = -5; - break; - } - iResult = -4; - } - else if( hb_znetInetInitialize( pItem, pStream, - hb_inetReadSSL, hb_inetWriteSSL, - hb_inetFlushSSL, hb_inetCloseSSL, - hb_inetErrorSSL, hb_inetErrStrSSL ) ) - break; - else + hb_ssl_socketClose( pStream ); iResult = -3; + } } - if( iResult != 1 ) - hb_inetCloseSSL( pStream ); - else - pStream->blocking = hb_socketSetBlockingIO( sd, HB_FALSE ) < 0; } hb_retni( iResult ); } diff --git a/contrib/hbssl/ssl_sock.c b/contrib/hbssl/ssl_sock.c new file mode 100644 index 0000000000..638e16b11f --- /dev/null +++ b/contrib/hbssl/ssl_sock.c @@ -0,0 +1,596 @@ +/* + * Harbour Project source code: + * Harbour extended socket filter with SSL encryption + * + * Copyright 2015 Przemyslaw Czerpak + * 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( , [, ] ) */ +HB_FUNC( HB_SOCKETNEWSSL_CONNECT ) +{ + s_sslSocketNew( HB_FALSE ); +} + +/* hb_socketNewSSL_accept( , [, ] ) */ +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 diff --git a/contrib/hbssl/tests/inetssl.prg b/contrib/hbssl/tests/inetssl.prg index f60aae6d7c..3bf8ff3fae 100644 --- a/contrib/hbssl/tests/inetssl.prg +++ b/contrib/hbssl/tests/inetssl.prg @@ -81,7 +81,7 @@ STATIC FUNCTION Client() ERR_error_string( nErr ) ) IF nResult == 1 ? "CLIENT: connected with", SSL_get_cipher( ssl ), "encryption." - DipsCertInfo( ssl, "CLIENT: " ) + DispCertInfo( ssl, "CLIENT: " ) hb_inetSendAll( sock, hb_TSToStr( hb_DateTime() ) + EOL ) DO WHILE ! Empty( cLine := hb_inetRecvLine( sock ) ) @@ -184,7 +184,7 @@ STATIC FUNCTION LoadCertificates( ssl_ctx, cCertFile, cKeyFile ) RETURN NIL -STATIC FUNCTION DipsCertInfo( ssl, cWho ) +STATIC FUNCTION DispCertInfo( ssl, cWho ) LOCAL cert IF ! Empty( cert := SSL_get_peer_certificate( ssl ) ) diff --git a/contrib/hbtcpio/tcpio.c b/contrib/hbtcpio/tcpio.c index e675791add..a148c0beb8 100644 --- a/contrib/hbtcpio/tcpio.c +++ b/contrib/hbtcpio/tcpio.c @@ -62,13 +62,13 @@ typedef struct _HB_FILE { const HB_FILE_FUNCS * pFuncs; - HB_SOCKET sd; + PHB_SOCKEX sock; HB_BOOL fEof; int timeout; } HB_FILE; -static PHB_FILE s_fileNew( HB_SOCKET sd, int timeout ); +static PHB_FILE s_fileNew( PHB_SOCKEX sock, int timeout ); static HB_BOOL s_fileAccept( PHB_FILE_FUNCS pFuncs, const char * pszFileName ) { @@ -133,6 +133,8 @@ static PHB_FILE s_fileOpen( PHB_FILE_FUNCS pFuncs, const char * pszName, hb_socketSetKeepAlive( sd, HB_TRUE ); if( hb_socketConnect( sd, pSockAddr, uiLen, timeout ) == 0 ) { + PHB_SOCKEX sock; + switch( nExFlags & ( FO_READ | FO_WRITE | FO_READWRITE ) ) { case FO_READ: @@ -142,8 +144,14 @@ static PHB_FILE s_fileOpen( PHB_FILE_FUNCS pFuncs, const char * pszName, hb_socketShutdown( sd, HB_SOCKET_SHUT_RD ); break; } - pFile = s_fileNew( sd, timeout ); - sd = HB_NO_SOCKET; + sock = hb_sockexNew( sd, NULL, NULL ); + if( sock ) + { + hb_sockexSetShutDown( sock, HB_TRUE ); + hb_sockexSetAutoFlush( sock, HB_TRUE ); + pFile = s_fileNew( sock, timeout ); + sd = HB_NO_SOCKET; + } } hb_xfree( pSockAddr ); } @@ -178,8 +186,7 @@ static PHB_FILE s_fileOpen( PHB_FILE_FUNCS pFuncs, const char * pszName, static void s_fileClose( PHB_FILE pFile ) { - hb_socketShutdown( pFile->sd, HB_SOCKET_SHUT_RDWR ); - hb_socketClose( pFile->sd ); + hb_sockexClose( pFile->sock, HB_TRUE ); hb_fsSetError( hb_socketGetError() ); hb_xfree( pFile ); } @@ -195,7 +202,7 @@ static HB_SIZE s_fileRead( PHB_FILE pFile, void * data, lRead = nSize > LONG_MAX ? LONG_MAX : ( long ) nSize; if( timeout == -1 ) timeout = pFile->timeout; - lRead = hb_socketRecv( pFile->sd, data, lRead, 0, timeout ); + lRead = hb_sockexRead( pFile->sock, data, lRead, timeout ); errcode = hb_socketGetError(); @@ -226,7 +233,7 @@ static HB_SIZE s_fileWrite( PHB_FILE pFile, const void * data, if( timeout == -1 ) timeout = pFile->timeout; - lSend = hb_socketSend( pFile->sd, data, lSend, 0, timeout ); + lSend = hb_sockexWrite( pFile->sock, data, lSend, timeout ); hb_fsSetError( hb_socketGetError() ); if( lSend < 0 ) @@ -241,6 +248,13 @@ static HB_BOOL s_fileEof( PHB_FILE pFile ) return pFile->fEof; } +static void s_fileFlush( PHB_FILE pFile, HB_BOOL fDirty ) +{ + HB_SYMBOL_UNUSED( fDirty ); + + hb_sockexFlush( pFile->sock, pFile->timeout, HB_FALSE ); +} + static HB_BOOL s_fileConfigure( PHB_FILE pFile, int iIndex, PHB_ITEM pValue ) { HB_SYMBOL_UNUSED( pFile ); @@ -252,7 +266,7 @@ static HB_BOOL s_fileConfigure( PHB_FILE pFile, int iIndex, PHB_ITEM pValue ) static HB_FHANDLE s_fileHandle( PHB_FILE pFile ) { - return ( HB_FHANDLE ) ( pFile ? pFile->sd : HB_NO_SOCKET ); + return ( HB_FHANDLE ) ( pFile ? hb_sockexGetHandle( pFile->sock ) : HB_NO_SOCKET ); } static HB_FILE_FUNCS s_fileFuncs = @@ -291,37 +305,37 @@ static HB_FILE_FUNCS s_fileFuncs = NULL, /* s_fileSeek */ NULL, /* s_fileSize */ s_fileEof, - NULL, /* s_fileFlush */ + s_fileFlush, NULL, /* s_fileCommit */ s_fileConfigure, s_fileHandle }; -static PHB_FILE s_fileNew( HB_SOCKET sd, int timeout ) +static PHB_FILE s_fileNew( PHB_SOCKEX sock, int timeout ) { PHB_FILE pFile = ( PHB_FILE ) hb_xgrab( sizeof( HB_FILE ) ); pFile->pFuncs = &s_fileFuncs; - pFile->sd = sd; + pFile->sock = sock; pFile->fEof = HB_FALSE; pFile->timeout = timeout; return pFile; } -static PHB_FILE hb_fileFromSocket( HB_SOCKET sd ) +static PHB_FILE hb_fileFromSocket( PHB_SOCKEX sock, HB_MAXINT timeout ) { - return sd != HB_NO_SOCKET ? s_fileNew( sd, -1 ) : NULL; + return sock && hb_sockexGetHandle( sock ) != HB_NO_SOCKET ? s_fileNew( sock, timeout ) : NULL; } HB_FUNC( HB_VFFROMSOCKET ) { - HB_SOCKET sd = hb_socketParam( 1 ); - PHB_FILE pFile = hb_fileFromSocket( sd ); + PHB_SOCKEX sock = hb_sockexParam( 1 ); + PHB_FILE pFile = hb_fileFromSocket( sock, hb_parnintdef( 2, -1 ) ); if( pFile ) { - hb_socketItemClear( hb_param( 1, HB_IT_POINTER ) ); + hb_sockexItemClear( hb_param( 1, HB_IT_POINTER ) ); hb_fileItemPut( hb_param( -1, HB_IT_ANY ), pFile ); } } diff --git a/include/harbour.hbx b/include/harbour.hbx index 01267335a7..ce5bd788be 100644 --- a/include/harbour.hbx +++ b/include/harbour.hbx @@ -783,12 +783,15 @@ DYNAMIC hb_SHA384 DYNAMIC hb_SHA512 DYNAMIC hb_Shadow DYNAMIC hb_socketAccept +DYNAMIC hb_socketAutoFlush DYNAMIC hb_socketBind DYNAMIC hb_socketClose DYNAMIC hb_socketConnect DYNAMIC hb_socketErrorString +DYNAMIC hb_socketFlush DYNAMIC hb_socketGetError DYNAMIC hb_socketGetFD +DYNAMIC hb_socketGetFilter DYNAMIC hb_socketGetHostName DYNAMIC hb_socketGetHosts DYNAMIC hb_socketGetIFaces @@ -798,7 +801,11 @@ DYNAMIC hb_socketGetRcvBufSize DYNAMIC hb_socketGetSndBufSize DYNAMIC hb_socketGetSockName DYNAMIC hb_socketListen +DYNAMIC hb_socketNewBFSock +DYNAMIC hb_socketNewZNet +DYNAMIC hb_socketNewZSock DYNAMIC hb_socketOpen +DYNAMIC hb_socketRead DYNAMIC hb_socketRecv DYNAMIC hb_socketRecvFrom DYNAMIC hb_socketResolveAddr @@ -812,6 +819,7 @@ DYNAMIC hb_socketSendTo DYNAMIC hb_socketSetBlockingIO DYNAMIC hb_socketSetBroadcast DYNAMIC hb_socketSetExclusiveAddr +DYNAMIC hb_socketSetFilter DYNAMIC hb_socketSetKeepAlive DYNAMIC hb_socketSetMulticast DYNAMIC hb_socketSetNoDelay @@ -819,6 +827,7 @@ DYNAMIC hb_socketSetRcvBufSize DYNAMIC hb_socketSetReuseAddr DYNAMIC hb_socketSetSndBufSize DYNAMIC hb_socketShutdown +DYNAMIC hb_socketWrite DYNAMIC hb_SToD DYNAMIC hb_SToT DYNAMIC hb_StrCDecode diff --git a/include/hbsocket.h b/include/hbsocket.h index cfb63fec38..c918eacf65 100644 --- a/include/hbsocket.h +++ b/include/hbsocket.h @@ -123,6 +123,84 @@ extern HB_EXPORT HB_SOCKET hb_socketItemGet( PHB_ITEM pItem ); extern HB_EXPORT PHB_ITEM hb_socketItemPut( PHB_ITEM pItem, HB_SOCKET sd ); extern HB_EXPORT void hb_socketItemClear( PHB_ITEM pItem ); +#define HB_SOCKET_FILTER_MAX 128 + +struct _HB_SOCKEX; +typedef struct _HB_SOCKEX * PHB_SOCKEX; + +#if defined( _HB_SOCKEX_IMPLEMENTATION_ ) + +typedef struct +{ + const char * pszName; + PHB_SOCKEX ( * New ) ( HB_SOCKET sd, PHB_ITEM pParams ); + PHB_SOCKEX ( * Next ) ( PHB_SOCKEX pSock, PHB_ITEM pParams ); + int ( * Close ) ( PHB_SOCKEX pSock, HB_BOOL fClose ); + long ( * Read ) ( PHB_SOCKEX pSock, void * data, long len, HB_MAXINT timeout ); + long ( * Write ) ( PHB_SOCKEX pSock, const void * data, long len, HB_MAXINT timeout ); + long ( * Flush ) ( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync ); + int ( * CanRead ) ( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ); + int ( * CanWrite ) ( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ); + char * ( * Name ) ( PHB_SOCKEX pSock ); + const char *( * ErrorStr ) ( PHB_SOCKEX pSock, int iError ); +} +HB_SOCKET_FILTER, * PHB_SOCKET_FILTER; + +typedef struct _HB_SOCKEX +{ + HB_SOCKET sd; + HB_BOOL fRedirAll; + HB_BOOL fShutDown; + int iAutoFlush; + long inbuffer; + long posbuffer; + long readahead; + HB_BYTE * buffer; + void * cargo; + const HB_SOCKET_FILTER * pFilter; +} +HB_SOCKEX; + +extern HB_EXPORT int hb_sockexRegister( const HB_SOCKET_FILTER * pFilter ); + +#endif /* _HB_SOCKEX_IMPLEMENTATION_ */ + +extern HB_EXPORT int hb_sockexClose( PHB_SOCKEX pSock, HB_BOOL fClose ); +extern HB_EXPORT long hb_sockexRead ( PHB_SOCKEX pSock, void * data, long len, HB_MAXINT timeout ); +extern HB_EXPORT long hb_sockexWrite( PHB_SOCKEX pSock, const void * data, long len, HB_MAXINT timeout ); +extern HB_EXPORT long hb_sockexFlush( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync ); + +extern HB_EXPORT int hb_sockexCanRead ( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ); +extern HB_EXPORT int hb_sockexCanWrite( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ); +extern HB_EXPORT int hb_sockexSelect( PHB_ITEM pArrayRD, HB_BOOL fSetRD, + PHB_ITEM pArrayWR, HB_BOOL fSetWR, + PHB_ITEM pArrayEX, HB_BOOL fSetEX, + HB_MAXINT timeout, HB_SOCKET_FUNC pFunc ); + +extern HB_EXPORT HB_BOOL hb_sockexIsRaw( PHB_SOCKEX pSock ); +extern HB_EXPORT int hb_sockexRawClear( PHB_SOCKEX pSock, HB_BOOL fClear ); +extern HB_EXPORT HB_SOCKET hb_sockexGetHandle( PHB_SOCKEX pSock ); +extern HB_EXPORT void hb_sockexClearHandle( PHB_SOCKEX pSock ); +extern HB_EXPORT void hb_sockexSetShutDown( PHB_SOCKEX pSock, HB_BOOL fShutDown ); +extern HB_EXPORT HB_BOOL hb_sockexGetShutDown( PHB_SOCKEX pSock ); +extern HB_EXPORT void hb_sockexSetAutoFlush( PHB_SOCKEX pSock, int iAutoFlush ); +extern HB_EXPORT int hb_sockexGetAutoFlush( PHB_SOCKEX pSock ); + +extern HB_EXPORT void hb_socekxParamsInit( PHB_SOCKEX pSock, PHB_ITEM pParams ); +extern HB_EXPORT void hb_socekxParamsGetStd( PHB_ITEM pParams, + const void ** pKeydata, int * pKeylen, + const void ** pIV, int * pIVlen, + int * pLevel, int * pStrategy ); +extern HB_EXPORT PHB_SOCKEX hb_sockexNew( HB_SOCKET sd, const char * pszFilter, PHB_ITEM pParams ); +extern HB_EXPORT PHB_SOCKEX hb_sockexNext( PHB_SOCKEX pSock, const char * pszFilter, PHB_ITEM pParams ); +extern HB_EXPORT char * hb_sockexName( PHB_SOCKEX pSock ); +extern HB_EXPORT const char * hb_sockexErrorStr( PHB_SOCKEX pSock, int iError ); +extern HB_EXPORT PHB_SOCKEX hb_sockexParam( int iParam ); +extern HB_EXPORT PHB_SOCKEX hb_sockexItemGet( PHB_ITEM pItem ); +extern HB_EXPORT PHB_ITEM hb_sockexItemPut( PHB_ITEM pItem, PHB_SOCKEX pSock ); +extern HB_EXPORT void hb_sockexItemClear( PHB_ITEM pItem ); +extern HB_EXPORT HB_BOOL hb_sockexItemSetFilter( PHB_ITEM pItem, const char * pszFilter, PHB_ITEM pParams ); + HB_EXTERN_END #endif /* HB_SOCKET_H_ */ diff --git a/include/hbznet.h b/include/hbznet.h index aa8a98ae13..915e8ce9b6 100644 --- a/include/hbznet.h +++ b/include/hbznet.h @@ -60,6 +60,8 @@ HB_EXTERN_BEGIN #define HB_INET_ERR_BUFFOVERRUN ( -3 ) #define HB_INET_ERR_CLOSEDSOCKET ( -4 ) +#define HB_ZNET_SOCK_ERROR_BASE 100 + #if defined( _HB_ZNET_INTERNAL_ ) struct _HB_ZNETSTREAM; typedef struct _HB_ZNETSTREAM * PHB_ZNETSTREAM; @@ -69,7 +71,7 @@ HB_EXTERN_BEGIN typedef long ( * HB_INET_RDFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, void *, long, HB_MAXINT ); typedef long ( * HB_INET_WRFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, const void *, long, HB_MAXINT, long * ); -typedef long ( * HB_INET_FLFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, HB_MAXINT ); +typedef long ( * HB_INET_FLFUNC ) ( PHB_ZNETSTREAM, HB_SOCKET, HB_MAXINT, HB_BOOL ); typedef void ( * HB_INET_CLFUNC ) ( PHB_ZNETSTREAM ); typedef int ( * HB_INET_ERFUNC ) ( PHB_ZNETSTREAM ); typedef const char * ( * HB_INET_ESFUNC ) ( PHB_ZNETSTREAM, int ); @@ -79,7 +81,7 @@ extern HB_EXPORT void hb_znetEncryptKey( PHB_ZNETSTREAM pStream, const void * extern HB_EXPORT void hb_znetClose( PHB_ZNETSTREAM pStream ); extern HB_EXPORT int hb_znetError( PHB_ZNETSTREAM pStream ); extern HB_EXPORT long hb_znetRead( PHB_ZNETSTREAM pStream, HB_SOCKET sd, void * buffer, long len, HB_MAXINT timeout ); -extern HB_EXPORT long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_MAXINT timeout ); +extern HB_EXPORT long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_MAXINT timeout, HB_BOOL fSync ); extern HB_EXPORT long hb_znetWrite( PHB_ZNETSTREAM pStream, HB_SOCKET sd, const void * buffer, long len, HB_MAXINT timeout, long * plast ); extern HB_EXPORT HB_SOCKET hb_znetInetFD( PHB_ITEM pItem, HB_BOOL fError ); @@ -92,6 +94,9 @@ extern HB_EXPORT HB_BOOL hb_znetInetInitialize( PHB_ITEM, PHB_ZNETSTREAM, HB_INET_ERFUNC, HB_INET_ESFUNC ); +extern HB_EXPORT PHB_SOCKEX hb_sockexNewZNet( HB_SOCKET sd, const void * keydata, int keylen, + int level, int strategy ); + HB_EXTERN_END #endif /* HB_ZNET_H_ */ diff --git a/src/rtl/Makefile b/src/rtl/Makefile index 7d50ced620..6472d624df 100644 --- a/src/rtl/Makefile +++ b/src/rtl/Makefile @@ -75,6 +75,7 @@ C_SOURCES := \ hbascii.c \ hbbffnc.c \ hbbfish.c \ + hbbfsock.c \ hbbit.c \ hbbyte.c \ hbcom.c \ @@ -117,6 +118,7 @@ C_SOURCES := \ hbzlibc.c \ hbzlibgz.c \ hbznet.c \ + hbzsock.c \ idle.c \ inkey.c \ inkeyapi.c \ diff --git a/src/rtl/hbbfsock.c b/src/rtl/hbbfsock.c new file mode 100644 index 0000000000..eac83a4b29 --- /dev/null +++ b/src/rtl/hbbfsock.c @@ -0,0 +1,370 @@ +/* + * Harbour Project source code: + * Harbour extended socket filter with ZLIB compression + * + * Copyright 2015 Przemyslaw Czerpak + * 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 "hbsocket.h" +#include "hbbfish.h" +#include "hbinit.h" + +#define HB_BFSOCK_READAHEAD 0x40 +#define HB_BFSOCK_WRBUFSIZE 4096 + +#define HB_BFSOCK_GET( p ) ( ( PHB_SOCKEX_BF ) p->cargo ) + +typedef struct +{ + PHB_SOCKEX sock; + + HB_BLOWFISH bf; + HB_BYTE encryptkey[ HB_BF_CIPHERBLOCK ]; + HB_BYTE decryptkey[ HB_BF_CIPHERBLOCK ]; + HB_BYTE encounter[ HB_BF_CIPHERBLOCK ]; + HB_BYTE decounter[ HB_BF_CIPHERBLOCK ]; + HB_BYTE buffer[ HB_BFSOCK_WRBUFSIZE ]; + long inbuffer; + int encoded; + int decoded; +} +HB_SOCKEX_BF, * PHB_SOCKEX_BF; + +static void s_bf_hash( const HB_BLOWFISH * bf, + HB_BYTE * vect, HB_BYTE * counter ) +{ + HB_U32 xl, xr, cl, cr; + + cl = xl = HB_GET_BE_UINT32( &counter[ 0 ] ); + cr = xr = HB_GET_BE_UINT32( &counter[ 4 ] ); + ++cr; + HB_PUT_BE_UINT32( &counter[ 4 ], cr ); + if( cr == 0 ) + { + ++cl; + HB_PUT_BE_UINT32( &counter[ 0 ], cl ); + } + hb_blowfishEncrypt( bf, &xl, &xr ); + HB_PUT_BE_UINT32( &vect[ 0 ], xl ); + HB_PUT_BE_UINT32( &vect[ 4 ], xr ); +} + +static long s_bf_send( PHB_SOCKEX_BF pBF, HB_MAXINT timeout ) +{ + long lSent = 0, len = pBF->inbuffer; + + while( lSent < len ) + { + long l = hb_sockexWrite( pBF->sock, pBF->buffer + lSent, len - lSent, timeout ); + if( l <= 0 ) + { + switch( hb_socketGetError() ) + { + case HB_SOCKET_ERR_TIMEOUT: + case HB_SOCKET_ERR_AGAIN: + case HB_SOCKET_ERR_TRYAGAIN: + break; + default: + lSent = -1; + break; + } + break; + } + lSent += l; + if( timeout > 0 ) + timeout = 0; + } + + if( lSent > 0 ) + { + if( lSent < len ) + memmove( pBF->buffer, pBF->buffer + lSent, len - lSent ); + pBF->inbuffer -= lSent; + } + + return lSent; +} + +/* socket filter */ + +static long s_sockexRead( PHB_SOCKEX pSock, void * data, long len, HB_MAXINT timeout ) +{ + PHB_SOCKEX_BF pBF = HB_BFSOCK_GET( pSock ); + long lRecv; + + if( pSock->inbuffer > 0 && len > 0 ) + { + lRecv = HB_MIN( pSock->inbuffer, len ); + memcpy( data, pSock->buffer + pSock->posbuffer, lRecv ); + if( ( pSock->inbuffer -= lRecv ) > 0 ) + pSock->posbuffer += lRecv; + else + pSock->posbuffer = 0; + } + else + { + lRecv = hb_sockexRead( pBF->sock, data, len, timeout ); + if( lRecv > 0 ) + { + HB_BYTE * pData = ( HB_BYTE * ) data; + long l; + + for( l = 0; l < lRecv; ++l ) + { + if( ( pBF->decoded & ( HB_BF_CIPHERBLOCK - 1 ) ) == 0 ) + { + s_bf_hash( &pBF->bf, pBF->decryptkey, pBF->decounter ); + pBF->decoded = 0; + } + pData[ l ] ^= pBF->decryptkey[ pBF->decoded++ ]; + } + } + } + return lRecv; +} + +static long s_sockexWrite( PHB_SOCKEX pSock, const void * data, long len, HB_MAXINT timeout ) +{ + PHB_SOCKEX_BF pBF = HB_BFSOCK_GET( pSock ); + const HB_BYTE * pData = ( const HB_BYTE * ) data; + long lWritten = 0, lDone; + + for( lDone = 0; lDone < len; ++lDone ) + { + if( pBF->inbuffer == HB_BFSOCK_WRBUFSIZE ) + { + lWritten = s_bf_send( pBF, timeout ); + if( lWritten <= 0 ) + break; + timeout = 0; + } + if( ( pBF->encoded & ( HB_BF_CIPHERBLOCK - 1 ) ) == 0 ) + { + s_bf_hash( &pBF->bf, pBF->encryptkey, pBF->encounter ); + pBF->encoded = 0; + } + pBF->buffer[ pBF->inbuffer++ ] = pData[ lDone ] ^ pBF->encryptkey[ pBF->encoded++ ]; + } + + return lWritten >= 0 ? lDone : lWritten; +} + +static long s_sockexFlush( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync ) +{ + PHB_SOCKEX_BF pBF = HB_BFSOCK_GET( pSock ); + + while( pBF->inbuffer > 0 ) + { + if( s_bf_send( pBF, timeout ) <= 0 ) + break; + } + return pBF->inbuffer + hb_sockexFlush( pBF->sock, timeout, fSync ); +} + +static int s_sockexCanRead( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) +{ + return pSock->inbuffer > 0 ? 1 : + hb_sockexCanRead( HB_BFSOCK_GET( pSock )->sock, fBuffer, timeout ); +} + +static int s_sockexCanWrite( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) +{ + return hb_sockexCanWrite( HB_BFSOCK_GET( pSock )->sock, fBuffer, timeout ); +} + +static char * s_sockexName( PHB_SOCKEX pSock ) +{ + char * pszName = hb_sockexIsRaw( HB_BFSOCK_GET( pSock )->sock ) ? NULL : + hb_sockexName( HB_BFSOCK_GET( pSock )->sock ); + if( pszName ) + { + char * pszFree = pszName; + pszName = hb_xstrcpy( NULL, pSock->pFilter->pszName, "|", pszName, NULL ); + hb_xfree( pszFree ); + } + else + pszName = hb_strdup( pSock->pFilter->pszName ); + + return pszName; +} + +static const char * s_sockexErrorStr( PHB_SOCKEX pSock, int iError ) +{ + return hb_sockexErrorStr( HB_BFSOCK_GET( pSock )->sock, iError ); +} + +static int s_sockexClose( PHB_SOCKEX pSock, HB_BOOL fClose ) +{ + PHB_SOCKEX_BF pBF = HB_BFSOCK_GET( pSock ); + int iResult = 0; + + if( pBF ) + { + if( pBF->sock ) + s_sockexFlush( pSock, HB_MAX( 15000, pSock->iAutoFlush ), HB_TRUE ); + + if( pBF->sock ) + { + if( pSock->fShutDown ) + pBF->sock->fShutDown = HB_TRUE; + if( pSock->iAutoFlush != 0 && pBF->sock->iAutoFlush == 0 ) + pBF->sock->iAutoFlush = pSock->iAutoFlush; + iResult = hb_sockexClose( pBF->sock, fClose ); + } + memset( pBF, 0, sizeof( *pBF ) ); + hb_xfree( pBF ); + } + /* call hb_sockexRawClear() with fClose = HB_FALSE because + hb_sockexClose() already closed real socket */ + hb_sockexRawClear( pSock, HB_FALSE ); + hb_xfree( pSock ); + + return iResult; +} + +static PHB_SOCKEX s_sockexNext( PHB_SOCKEX pSock, PHB_ITEM pParams ); + +static PHB_SOCKEX s_sockexNew( HB_SOCKET sd, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSock, pSockNew = NULL; + + pSock = hb_sockexNew( sd, NULL, pParams ); + if( pSock ) + { + pSockNew = s_sockexNext( pSock, pParams ); + if( pSockNew == NULL ) + hb_sockexClose( pSock, HB_FALSE ); + } + + return pSockNew; +} + +static const HB_SOCKET_FILTER s_sockFilter = +{ + "BFSOCK", + s_sockexNew, + s_sockexNext, + s_sockexClose, + s_sockexRead, + s_sockexWrite, + s_sockexFlush, + s_sockexCanRead, + s_sockexCanWrite, + s_sockexName, + s_sockexErrorStr +}; + +static PHB_SOCKEX s_sockexNext( PHB_SOCKEX pSock, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSockNew = NULL; + + if( pSock ) + { + const void * keydata = NULL, * iv = NULL; + int keylen = 0, ivlen = 0; + + hb_socekxParamsGetStd( pParams, &keydata, &keylen, &iv, &ivlen, NULL, NULL ); + if( keylen > 0 ) + { + PHB_SOCKEX_BF pBF = ( PHB_SOCKEX_BF ) hb_xgrabz( sizeof( HB_SOCKEX_BF ) ); + HB_BYTE * pVect = ( HB_BYTE * ) ( ivlen > 0 ? iv : NULL ); + int i; + + hb_blowfishInit( &pBF->bf, keydata, keylen ); + for( i = 0; i < HB_BF_CIPHERBLOCK; ++i ) + { + if( pVect && ivlen > 0 ) + pBF->encounter[ i ] = + pBF->decounter[ i ] = pVect[ i % ivlen ]; + else + pBF->encounter[ i ] = + pBF->decounter[ i ] = ( HB_BYTE ) i; + } + + pSockNew = ( PHB_SOCKEX ) hb_xgrabz( sizeof( HB_SOCKEX ) ); + pSockNew->sd = pSock->sd; + pSockNew->fRedirAll = HB_TRUE; + pSockNew->fShutDown = pSock->fShutDown; + pSockNew->iAutoFlush = pSock->iAutoFlush; + pSockNew->pFilter = &s_sockFilter; + pSockNew->cargo = ( void * ) pBF; + pBF->sock = pSock; + hb_socekxParamsInit( pSockNew, pParams ); + } + } + + return pSockNew; +} + +/* hb_socketNewBFSock( , [] ) -> */ +HB_FUNC( HB_SOCKETNEWBFSOCK ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + { + pSock = s_sockexNext( pSock, hb_param( 2, HB_IT_HASH ) ); + if( pSock ) + { + hb_sockexItemClear( hb_param( 1, HB_IT_POINTER ) ); + hb_sockexItemPut( hb_param( -1, HB_IT_ANY ), pSock ); + } + } +} + +HB_CALL_ON_STARTUP_BEGIN( _hb_bfsock_init_ ) + hb_sockexRegister( &s_sockFilter ); +HB_CALL_ON_STARTUP_END( _hb_bfsock_init_ ) + +#if defined( HB_PRAGMA_STARTUP ) + #pragma startup _hb_bfsock_init_ +#elif defined( HB_DATASEG_STARTUP ) + #define HB_DATASEG_BODY HB_DATASEG_FUNC( _hb_bfsock_init_ ) + #include "hbiniseg.h" +#endif diff --git a/src/rtl/hbinet.c b/src/rtl/hbinet.c index c7f4879d3c..0560b96863 100644 --- a/src/rtl/hbinet.c +++ b/src/rtl/hbinet.c @@ -164,6 +164,10 @@ static void hb_inetGetError( PHB_SOCKET_STRUCT socket ) static void hb_inetCloseStream( PHB_SOCKET_STRUCT socket ) { + if( socket->flushFunc && socket->sd != HB_NO_SOCKET ) + socket->flushFunc( socket->stream, socket->sd, + HB_MAX( socket->iTimeout, 10000 ), HB_TRUE ); + if( socket->cleanFunc ) socket->cleanFunc( socket->stream ); @@ -1081,7 +1085,8 @@ static void s_inetSendInternal( HB_BOOL lAll ) /* TODO: safe information about unflushed data and try to call flush before entering receive wait sate */ socket->flushFunc( socket->stream, socket->sd, socket->iTimeout < 0 ? - socket->iTimeout : HB_MAX( socket->iTimeout, 10000 ) ); + socket->iTimeout : HB_MAX( socket->iTimeout, 10000 ), + HB_FALSE ); } hb_retni( iSent > 0 ? iSent : iLen ); diff --git a/src/rtl/hbsockhb.c b/src/rtl/hbsockhb.c index 1a02dbd088..e71b9f300e 100644 --- a/src/rtl/hbsockhb.c +++ b/src/rtl/hbsockhb.c @@ -49,7 +49,7 @@ /* * hb_socketGetError() --> nSocketError * hb_socketGetOSError() --> nOSError - * hb_socketErrorString( [ nSocketErrror = hb_socketGetError() ] ) --> cError + * hb_socketErrorString( [ nSocketErrror = hb_socketGetError() ], [ hSocket ] ) --> cError * hb_socketGetSockName( hSocket ) --> aAddr | NIL * hb_socketGetPeerName( hSocket ) --> aAddr | NIL * hb_socketOpen( [ nDomain = HB_SOCKET_AF_INET ], [ nType = HB_SOCKET_PT_STREAM ], [ nProtocol = 0 ] ) --> hSocket @@ -84,8 +84,17 @@ * hb_socketGetHosts( cAddr, [ nFamily = HB_SOCKET_AF_INET ] ) --> aHosts * hb_socketGetIFaces( [ nFamily ], [ lNoAliases ] ) --> aIfaces * hb_socketGetFD( hSocket ) --> nFD + * hb_socketSetFilter( hSocket, cFilterName, [ hSockParams ] ) --> hSocket + * hb_socketGetFilter( hSocket ) --> cFilterName + * hb_socketRead( hSocket, @cBuffer, [ nLen = Len( cBuffer ) ], [ nTimeout = FOREVER ] ) --> nBytesRead + * hb_socketWrite( hSocket, cBuffer, [ nLen = Len( cBuffer ) ], [ nTimeout = FOREVER ] ) --> nBytesWritten + * hb_socketFlush( hSocket, [ nTimeout = FOREVER ], [ lSync ] ) --> nBytesLeft + * hb_socketAutoFlush( hSocket, [ nNewSetting ] ) --> nPrevSetting */ +/* this has to be declared before hbsocket.h is included */ +#define _HB_SOCKEX_IMPLEMENTATION_ + #include "hbapiitm.h" #include "hbapierr.h" #include "hbvm.h" @@ -94,16 +103,264 @@ static HB_BOOL s_fInit = HB_FALSE; +/* create new extended socket structure */ +static PHB_SOCKEX s_sockexNew( HB_SOCKET sd, PHB_ITEM pParams ); + +/* destroy extended socket structure */ +static int s_sockexClose( PHB_SOCKEX pSock, HB_BOOL fClose ) +{ + int iResult = hb_sockexRawClear( pSock, fClose ); + + hb_xfree( pSock ); + + return iResult; +} + +/* read data from extended socket, check internal readahead + buffer first */ +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; + } + if( pSock->sd == HB_NO_SOCKET ) + { + hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE ); + return -1; + } + return hb_socketRecv( pSock->sd, data, len, 0, timeout ); +} + +/* write data to extended socket */ +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_socketSend( pSock->sd, data, len, 0, timeout ); +} + +/* flush data written to extended socket which may not be sent so far, + return number of bytes not flushed (still in sent buffer), parameter + fSync is set to TRUE if this function should use special flush method + which allows to synchronize with new filter initialized from scratch, + i.e. it's necessary to inform peer that we replace ZLIB compressor, + core code set it to true only just before closing the filter. */ +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; +} + +/* 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 ); +} + +/* check if data can be written to extended socket without any delay, + return 1, 0 or -1 to indicate error. + If fBuffer parameter is set to true then only this function should + check only free place in internal sent buffer if such buffer exists. + Real socket device should be checked only when fBuffer is false. + If extended socket does not use any sent buffers and fBuffer is true + then this functions should return 0. In most of implementations 0 + can be returned in all cases if fBuffer is true. Such behavior will + reduce number of data buffered and not flushed. */ +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 ); +} + +/* return socket name, caller is responsible to free the buffer + it's intentionally non static buffer to allow nested filters + create complex filter name dynamically, i.e. "zlib|blowfish" + */ +static char * s_sockexName( PHB_SOCKEX pSock ) +{ + return hb_strdup( pSock->pFilter->pszName ); +} + +/* convert error code into short string description */ +static const char * s_sockexErrorStr( PHB_SOCKEX pSock, int iError ) +{ + HB_SYMBOL_UNUSED( pSock ); + + return hb_socketErrorStr( iError ); +} + +/* this is basic wrapper which does not support multilevel filtering + so it destroys previous wrappers if any and restore new raw socket, + some other wrappers may allow to join filters encapsulating existing + ones, i.e.: "ZLIB" | "BlowFish" */ +static PHB_SOCKEX s_sockexNext( PHB_SOCKEX pSock, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSockNew = NULL; + + if( pSock ) + { + pSockNew = s_sockexNew( pSock->sd, pParams ); + if( pSockNew ) + hb_sockexClose( pSock, HB_FALSE ); + } + + return pSockNew; +} + +static const HB_SOCKET_FILTER s_sockFilter = +{ + "socket", + s_sockexNew, + s_sockexNext, + s_sockexClose, + s_sockexRead, + s_sockexWrite, + s_sockexFlush, + s_sockexCanRead, + s_sockexCanWrite, + s_sockexName, + s_sockexErrorStr +}; + +/* create new extended socket structure */ +static PHB_SOCKEX s_sockexNew( HB_SOCKET sd, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSock; + + pSock = ( PHB_SOCKEX ) hb_xgrabz( sizeof( HB_SOCKEX ) ); + pSock->sd = sd; + pSock->pFilter = &s_sockFilter; + + hb_socekxParamsInit( pSock, pParams ); + + return pSock; +} + +static const HB_SOCKET_FILTER * s_socketFilters[ HB_SOCKET_FILTER_MAX ]; +static int s_iFilterCount = 0; + +int hb_sockexRegister( const HB_SOCKET_FILTER * pFilter ) +{ + if( s_iFilterCount == 0 ) + s_socketFilters[ s_iFilterCount++ ] = &s_sockFilter; + + if( pFilter ) + { + int i; + + for( i = 0; i < s_iFilterCount; ++i ) + { + if( s_socketFilters[ i ] == pFilter ) + return 1; + if( hb_stricmp( s_socketFilters[ i ]->pszName, pFilter->pszName ) == 0 ) + return 2; + } + + if( s_iFilterCount >= HB_SOCKET_FILTER_MAX ) + return 3; + + s_socketFilters[ s_iFilterCount++ ] = pFilter; + } + + return 0; +} + +/* helper functions */ + +static HB_BOOL s_socketaddrParam( int iParam, void ** pAddr, unsigned int * puiLen ) +{ + PHB_ITEM pItem = hb_param( iParam, HB_IT_ARRAY ); + + if( pItem && hb_socketAddrFromItem( pAddr, puiLen, pItem ) ) + return HB_TRUE; + + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + return HB_FALSE; +} + +static HB_SOCKET s_socketSelectCallback( PHB_ITEM pItem ) +{ + HB_SOCKET socket = hb_socketItemGet( pItem ); + + if( socket != HB_NO_SOCKET ) + return socket; + + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + return HB_NO_SOCKET; +} + + +static void s_socket_exit( void * cargo ) +{ + HB_SYMBOL_UNUSED( cargo ); + + if( s_fInit ) + { + hb_socketCleanup(); + s_iFilterCount = 0; + s_fInit = HB_FALSE; + } +} + +static void s_socket_init( void ) +{ + if( ! s_fInit ) + { + hb_sockexRegister( NULL ); + hb_socketInit(); + hb_vmAtQuit( s_socket_exit, NULL ); + s_fInit = HB_TRUE; + } +} + +void hb_socketAutoInit( void ) +{ + s_socket_init(); +} + + /* Collectable pointer support */ static HB_GARBAGE_FUNC( hb_socket_destructor ) { - HB_SOCKET * pSocket = ( HB_SOCKET * ) Cargo; + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) Cargo; - if( *pSocket != HB_NO_SOCKET ) + if( *pSockPtr ) { - hb_socketClose( *pSocket ); - *pSocket = HB_NO_SOCKET; + hb_sockexClose( *pSockPtr, HB_TRUE ); + *pSockPtr = NULL; } } @@ -115,10 +372,10 @@ static const HB_GC_FUNCS s_gcSocketFuncs = HB_SOCKET hb_socketParam( int iParam ) { - HB_SOCKET * pSocket = ( HB_SOCKET * ) hb_parptrGC( &s_gcSocketFuncs, iParam ); + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_parptrGC( &s_gcSocketFuncs, iParam ); - if( pSocket && *pSocket != HB_NO_SOCKET ) - return *pSocket; + if( pSockPtr && *pSockPtr ) + return ( *pSockPtr )->sd; hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); return HB_NO_SOCKET; @@ -126,65 +383,456 @@ HB_SOCKET hb_socketParam( int iParam ) HB_SOCKET hb_socketItemGet( PHB_ITEM pItem ) { - HB_SOCKET * pSocket = ( HB_SOCKET * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); - return pSocket ? *pSocket : HB_NO_SOCKET; + return pSockPtr && *pSockPtr ? ( *pSockPtr )->sd : HB_NO_SOCKET; } PHB_ITEM hb_socketItemPut( PHB_ITEM pItem, HB_SOCKET sd ) { - HB_SOCKET * pSocket = ( HB_SOCKET * ) hb_gcAllocate( sizeof( HB_SOCKET ), &s_gcSocketFuncs ); + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_gcAllocate( sizeof( PHB_SOCKEX ), &s_gcSocketFuncs ); - *pSocket = sd; - return hb_itemPutPtrGC( pItem, pSocket ); + *pSockPtr = hb_sockexNew( sd, NULL, NULL ); + + return hb_itemPutPtrGC( pItem, pSockPtr ); } void hb_socketItemClear( PHB_ITEM pItem ) { - HB_SOCKET * pSocket = ( HB_SOCKET * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); - if( pSocket ) - *pSocket = HB_NO_SOCKET; + if( pSockPtr && *pSockPtr ) + { + hb_sockexClose( *pSockPtr, HB_FALSE ); + *pSockPtr = NULL; + } } +/* extended socket functions hb_sockex* */ -static HB_BOOL socketaddrParam( int iParam, void ** pAddr, unsigned int * puiLen ) +PHB_SOCKEX hb_sockexParam( int iParam ) { - PHB_ITEM pItem = hb_param( iParam, HB_IT_ARRAY ); + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_parptrGC( &s_gcSocketFuncs, iParam ); - if( pItem && hb_socketAddrFromItem( pAddr, puiLen, pItem ) ) - return HB_TRUE; + if( pSockPtr && *pSockPtr ) + return *pSockPtr; hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + return NULL; +} + +PHB_SOCKEX hb_sockexItemGet( PHB_ITEM pItem ) +{ + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); + + return pSockPtr ? *pSockPtr : NULL; +} + +PHB_ITEM hb_sockexItemPut( PHB_ITEM pItem, PHB_SOCKEX pSock ) +{ + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_gcAllocate( sizeof( PHB_SOCKEX ), &s_gcSocketFuncs ); + + *pSockPtr = pSock; + + return hb_itemPutPtrGC( pItem, pSockPtr ); +} + +void hb_sockexItemClear( PHB_ITEM pItem ) +{ + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); + + if( pSockPtr && *pSockPtr ) + *pSockPtr = NULL; +} + +HB_BOOL hb_sockexItemSetFilter( PHB_ITEM pItem, const char * pszFilter, PHB_ITEM pParams ) +{ + PHB_SOCKEX * pSockPtr = ( PHB_SOCKEX * ) hb_itemGetPtrGC( pItem, &s_gcSocketFuncs ); + + if( pSockPtr && *pSockPtr ) + { + PHB_SOCKEX pSock = *pSockPtr; + + if( pszFilter == NULL ? pSock->pFilter == &s_sockFilter : + ! hb_stricmp( pSock->pFilter->pszName, pszFilter ) ) + return HB_TRUE; + else + { + PHB_SOCKEX pSockNew = hb_sockexNext( pSock, pszFilter, pParams ); + + if( pSockNew ) + { + *pSockPtr = pSockNew; + return HB_TRUE; + } + } + } return HB_FALSE; } - -static void s_socket_exit( void * cargo ) +static int s_socket_filter_find( const char * pszFilter ) { - HB_SYMBOL_UNUSED( cargo ); + int i; - if( s_fInit ) + for( i = 0; i < s_iFilterCount; ++i ) { - hb_socketCleanup(); - s_fInit = HB_FALSE; + if( hb_stricmp( s_socketFilters[ i ]->pszName, pszFilter ) == 0 ) + return i; + } + return -1; +} + +static const HB_SOCKET_FILTER ** s_socket_getfilters( const char * pszFilter, + const HB_SOCKET_FILTER ** pFilters, + int * piCount ) +{ + int iCount = 0, iMax = *piCount; + + if( pszFilter == NULL ) + pFilters[ iCount++ ] = &s_sockFilter; + else + { + int i = s_socket_filter_find( pszFilter ); + + if( i >= 0 ) + pFilters[ iCount++ ] = s_socketFilters[ i ]; + else if( strchr( pszFilter, '|' ) != NULL ) + { + char * pszFilterList, * ptr; + + pszFilterList = hb_strdup( pszFilter ); + ptr = pszFilterList + strlen( pszFilterList ); + + while( ptr-- > pszFilterList ) + { + i = -2; + if( *ptr == '|' ) + { + *ptr = '\0'; + if( ptr[ 1 ] ) + i = s_socket_filter_find( ptr + 1 ); + } + else if( ptr == pszFilterList && ptr[ 0 ] ) + i = s_socket_filter_find( ptr ); + + if( i >= 0 ) + { + if( iCount == iMax ) + { + if( iMax == *piCount ) + pFilters = ( const HB_SOCKET_FILTER ** ) + hb_xmemdup( pFilters, sizeof( *pFilters ) * iMax ); + iMax += 16; + pFilters = ( const HB_SOCKET_FILTER ** ) + hb_xrealloc( pFilters, sizeof( *pFilters ) * iMax ); + } + pFilters[ iCount++ ] = s_socketFilters[ i ]; + } + else if( i == -1 ) + { + iCount = 0; + break; + } + } + hb_xfree( pszFilterList ); + } + } + if( iCount == 0 ) + { + if( iMax > *piCount ) + hb_xfree( pFilters ); + pFilters = NULL; + } + *piCount = iCount; + + return pFilters; +} + +PHB_SOCKEX hb_sockexNew( HB_SOCKET sd, const char * pszFilter, PHB_ITEM pParams ) +{ + const HB_SOCKET_FILTER * pBuffer[ 16 ]; + const HB_SOCKET_FILTER ** pFilters; + int iCount = HB_SIZEOFARRAY( pBuffer ); + PHB_SOCKEX pSock = NULL; + + pFilters = s_socket_getfilters( pszFilter, pBuffer, &iCount ); + if( pFilters ) + { + int i; + + for( i = 0; i < iCount; ++i ) + { + PHB_SOCKEX pSockNew = pSock == NULL ? + pFilters[ i ]->New( sd, pParams ) : + pFilters[ i ]->Next( pSock, pParams ); + if( pSockNew == NULL ) + { + hb_sockexClose( pSock, HB_FALSE ); + pSock = NULL; + break; + } + pSock = pSockNew; + } + if( pFilters != pBuffer ) + hb_xfree( pFilters ); + } + return pSock; +} + +PHB_SOCKEX hb_sockexNext( PHB_SOCKEX pSock, const char * pszFilter, PHB_ITEM pParams ) +{ + const HB_SOCKET_FILTER * pBuffer[ 16 ]; + const HB_SOCKET_FILTER ** pFilters; + int iCount = HB_SIZEOFARRAY( pBuffer ); + + pFilters = s_socket_getfilters( pszFilter, pBuffer, &iCount ); + if( pFilters ) + { + int i; + + for( i = 0; pSock && i < iCount; ++i ) + { + PHB_SOCKEX pSockNew = pFilters[ i ]->Next( pSock, pParams ); + if( pSockNew != NULL ) + pSock = pSockNew; + else if( i == 0 ) + pSock = NULL; + } + if( pFilters != pBuffer ) + hb_xfree( pFilters ); + } + return pSock; +} + +int hb_sockexClose( PHB_SOCKEX pSock, HB_BOOL fClose ) +{ + return pSock->pFilter->Close( pSock, fClose ); +} + +long hb_sockexRead( PHB_SOCKEX pSock, void * data, long len, HB_MAXINT timeout ) +{ + return pSock->pFilter->Read( pSock, data, len, timeout ); +} + +long hb_sockexWrite( PHB_SOCKEX pSock, const void * data, long len, HB_MAXINT timeout ) +{ + len = pSock->pFilter->Write( pSock, data, len, timeout ); + if( len >= 0 && pSock->iAutoFlush > 0 ) + { + if( timeout >= 0 ) + timeout = HB_MAX( timeout, pSock->iAutoFlush ); + hb_sockexFlush( pSock, timeout, HB_FALSE ); + } + return len; +} + +long hb_sockexFlush( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync ) +{ + return pSock->pFilter->Flush( pSock, timeout, fSync ); +} + +int hb_sockexCanRead( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) +{ + return pSock->pFilter->CanRead( pSock, fBuffer, timeout ); +} + +int hb_sockexCanWrite( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) +{ + return pSock->pFilter->CanWrite( pSock, fBuffer, timeout ); +} + +char * hb_sockexName( PHB_SOCKEX pSock ) +{ + return pSock->pFilter->Name( pSock ); +} + +const char * hb_sockexErrorStr( PHB_SOCKEX pSock, int iError ) +{ + return pSock->pFilter->ErrorStr( pSock, iError ); +} + +int hb_sockexSelect( PHB_ITEM pArrayRD, HB_BOOL fSetRD, + PHB_ITEM pArrayWR, HB_BOOL fSetWR, + PHB_ITEM pArrayEX, HB_BOOL fSetEX, + HB_MAXINT timeout, HB_SOCKET_FUNC pFunc ) +{ + int iResult; + HB_SIZE nRead = 0, nWrite = 0, nLen, nPos; + PHB_SOCKEX pSock; + + if( pArrayRD ) + { + nLen = hb_arrayLen( pArrayRD ); + for( nPos = 1; nPos <= nLen; ++nPos ) + { + pSock = hb_sockexItemGet( hb_arrayGetItemPtr( pArrayRD, nPos ) ); + if( pSock && pSock->pFilter->CanRead( pSock, HB_TRUE, 0 ) > 0 ) + { + ++nRead; + if( fSetRD && nRead != nPos ) + hb_itemMove( hb_arrayGetItemPtr( pArrayRD, nRead ), + hb_arrayGetItemPtr( pArrayRD, nPos ) ); + } + } + } + if( pArrayWR ) + { + nLen = hb_arrayLen( pArrayWR ); + for( nPos = 1; nPos <= nLen; ++nPos ) + { + pSock = hb_sockexItemGet( hb_arrayGetItemPtr( pArrayWR, nPos ) ); + if( pSock && pSock->pFilter->CanWrite( pSock, HB_TRUE, 0 ) > 0 ) + { + ++nWrite; + if( fSetWR && nWrite != nPos ) + hb_itemMove( hb_arrayGetItemPtr( pArrayWR, nWrite ), + hb_arrayGetItemPtr( pArrayWR, nPos ) ); + } + } + } + + if( nRead > 0 || nWrite > 0 ) + { + if( fSetRD && pArrayRD ) + hb_arraySize( pArrayRD, nRead ); + if( fSetWR && pArrayWR ) + hb_arraySize( pArrayWR, nWrite ); + if( fSetEX && pArrayEX ) + hb_arraySize( pArrayEX, 0 ); + iResult = ( int ) ( nRead + nWrite ); + } + else + { + if( pFunc == NULL ) + pFunc = s_socketSelectCallback; + iResult = hb_socketSelect( pArrayRD, fSetRD, pArrayWR, fSetWR, pArrayEX, fSetEX, + timeout, pFunc ); + } + + return iResult; +} + +int hb_sockexRawClear( PHB_SOCKEX pSock, HB_BOOL fClose ) +{ + int iResult = 0; + + if( fClose && pSock->sd != HB_NO_SOCKET ) + { + if( pSock->fShutDown ) + hb_socketShutdown( pSock->sd, HB_SOCKET_SHUT_RDWR ); + iResult = hb_socketClose( pSock->sd ); + } + if( pSock->buffer ) + hb_xfree( pSock->buffer ); + + memset( pSock, 0, sizeof( *pSock ) ); + pSock->sd = HB_NO_SOCKET; + + return iResult; +} + +HB_BOOL hb_sockexIsRaw( PHB_SOCKEX pSock ) +{ + return pSock && pSock->pFilter == &s_sockFilter; +} + +HB_SOCKET hb_sockexGetHandle( PHB_SOCKEX pSock ) +{ + return pSock ? pSock->sd : HB_NO_SOCKET; +} + +void hb_sockexClearHandle( PHB_SOCKEX pSock ) +{ + if( pSock ) + pSock->sd = HB_NO_SOCKET; +} + +HB_BOOL hb_sockexGetShutDown( PHB_SOCKEX pSock ) +{ + return pSock && pSock->fShutDown; +} + +void hb_sockexSetShutDown( PHB_SOCKEX pSock, HB_BOOL fShutDown ) +{ + if( pSock ) + pSock->fShutDown = fShutDown; +} + +int hb_sockexGetAutoFlush( PHB_SOCKEX pSock ) +{ + return pSock ? pSock->iAutoFlush : 0; +} + +void hb_sockexSetAutoFlush( PHB_SOCKEX pSock, int iAutoFlush ) +{ + if( pSock ) + pSock->iAutoFlush = iAutoFlush; +} + +void hb_socekxParamsGetStd( PHB_ITEM pParams, + const void ** pKeydata, int * pKeylen, + const void ** pIV, int * pIVlen, + int * pLevel, int * pStrategy ) +{ + if( pParams && HB_IS_HASH( pParams ) ) + { + PHB_ITEM pItem; + + if( pKeydata && pKeylen && + ( pItem = hb_hashGetCItemPtr( pParams, "key" ) ) != NULL && + HB_IS_STRING( pItem ) ) + { + *pKeydata = hb_itemGetCPtr( pItem ); + *pKeylen = hb_itemGetCLen( pItem ); + } + else if( pKeydata && pKeylen && + ( pItem = hb_hashGetCItemPtr( pParams, "pass" ) ) != NULL && + HB_IS_STRING( pItem ) ) + { + *pKeydata = hb_itemGetCPtr( pItem ); + *pKeylen = hb_itemGetCLen( pItem ); + } + if( pIV && pIVlen && + ( pItem = hb_hashGetCItemPtr( pParams, "iv" ) ) != NULL && + HB_IS_STRING( pItem ) ) + { + *pIV = hb_itemGetCPtr( pItem ); + *pIVlen = hb_itemGetCLen( pItem ); + } + if( pLevel && + ( pItem = hb_hashGetCItemPtr( pParams, "zlib" ) ) != NULL && + HB_IS_NUMERIC( pItem ) ) + *pLevel = hb_itemGetNI( pItem ); + if( pStrategy && + ( pItem = hb_hashGetCItemPtr( pParams, "zs" ) ) != NULL && + HB_IS_NUMERIC( pItem ) ) + *pStrategy = hb_itemGetNI( pItem ); } } -static void s_socket_init( void ) +void hb_socekxParamsInit( PHB_SOCKEX pSock, PHB_ITEM pParams ) { - if( ! s_fInit ) + if( pParams && HB_IS_HASH( pParams ) ) { - hb_socketInit(); - hb_vmAtQuit( s_socket_exit, NULL ); - s_fInit = HB_TRUE; + PHB_ITEM pItem; + + if( ( pItem = hb_hashGetCItemPtr( pParams, "readahead" ) ) != NULL && + HB_IS_NUMERIC( pItem ) ) + { + if( pSock->buffer == NULL ) + pSock->readahead = hb_itemGetNL( pItem ); + } + if( ( pItem = hb_hashGetCItemPtr( pParams, "flush" ) ) != NULL && + HB_IS_NUMERIC( pItem ) ) + pSock->iAutoFlush = hb_itemGetNI( pItem ); + if( ( pItem = hb_hashGetCItemPtr( pParams, "redir" ) ) != NULL && + HB_IS_LOGICAL( pItem ) ) + pSock->fRedirAll = hb_itemGetL( pItem ); } } -void hb_socketAutoInit( void ) -{ - s_socket_init(); -} + +/* PRG functions */ HB_FUNC( HB_SOCKETGETERROR ) { @@ -198,10 +846,31 @@ HB_FUNC( HB_SOCKETGETOSERROR ) HB_FUNC( HB_SOCKETERRORSTRING ) { - if( HB_ISNUM( 1 ) ) - hb_retc( hb_socketErrorStr( hb_parni( 1 ) ) ); + PHB_SOCKEX pSock; + int iError = 1; + + if( HB_ISPOINTER( 1 ) ) + pSock = hb_sockexParam( 1 ); + else if( HB_ISPOINTER( 2 ) ) + pSock = hb_sockexParam( 2 ); else - hb_retc( hb_socketErrorStr( hb_socketGetError() ) ); + { + pSock = NULL; + iError = 0; + } + + if( pSock || iError == 0 ) + { + if( HB_ISNUM( 1 ) ) + iError = hb_parni( 1 ); + else if( HB_ISNUM( 2 ) ) + iError = hb_parni( 2 ); + else + iError = hb_socketGetError(); + + hb_retc( pSock ? hb_sockexErrorStr( pSock, iError ) : + hb_socketErrorStr( iError ) ); + } } HB_FUNC( HB_SOCKETGETSOCKNAME ) @@ -272,12 +941,12 @@ HB_FUNC( HB_SOCKETOPEN ) HB_FUNC( HB_SOCKETCLOSE ) { - HB_SOCKET socket = hb_socketParam( 1 ); + PHB_SOCKEX pSock = hb_sockexParam( 1 ); - if( socket != HB_NO_SOCKET ) + if( pSock ) { - hb_socketItemClear( hb_param( 1, HB_IT_POINTER ) ); - hb_retl( hb_socketClose( socket ) == 0 ); + hb_sockexItemClear( hb_param( 1, HB_IT_POINTER ) ); + hb_retl( hb_sockexClose( pSock, HB_TRUE ) == 0 ); } } @@ -295,7 +964,7 @@ HB_FUNC( HB_SOCKETBIND ) void * addr; unsigned int len; - if( socket != HB_NO_SOCKET && socketaddrParam( 2, &addr, &len ) ) + if( socket != HB_NO_SOCKET && s_socketaddrParam( 2, &addr, &len ) ) { hb_retl( hb_socketBind( socket, addr, len ) == 0 ); hb_xfree( addr ); @@ -323,7 +992,8 @@ HB_FUNC( HB_SOCKETACCEPT ) socketaccept = hb_socketAccept( socket, &addr, &len, hb_parnintdef( 3, -1 ) ); if( socketaccept != HB_NO_SOCKET ) - hb_socketItemPut( hb_stackReturnItem(), socketaccept ); + hb_sockexSetShutDown( hb_sockexItemGet( hb_socketItemPut( hb_stackReturnItem(), + socketaccept ) ), HB_TRUE ); else hb_retptr( NULL ); @@ -351,20 +1021,26 @@ HB_FUNC( HB_SOCKETCONNECT ) void * addr; unsigned int len; - if( socket != HB_NO_SOCKET && socketaddrParam( 2, &addr, &len ) ) + if( socket != HB_NO_SOCKET && s_socketaddrParam( 2, &addr, &len ) ) { - hb_retl( hb_socketConnect( socket, addr, len, hb_parnintdef( 3, -1 ) ) == 0 ); + HB_BOOL fResult = hb_socketConnect( socket, addr, len, hb_parnintdef( 3, -1 ) ) == 0; + + if( fResult ) + hb_sockexSetShutDown( hb_sockexItemGet( hb_param( 1, HB_IT_POINTER ) ), HB_TRUE ); + hb_retl( fResult ); hb_xfree( addr ); } } HB_FUNC( HB_SOCKETSEND ) { - HB_SOCKET socket = hb_socketParam( 1 ); + PHB_SOCKEX pSock = hb_sockexParam( 1 ); - if( socket != HB_NO_SOCKET ) + if( pSock ) { long lLen = ( long ) hb_parclen( 2 ); + const char * data = hb_parc( 2 ); + HB_MAXINT timeout = hb_parnintdef( 5, -1 ); if( HB_ISNUM( 3 ) ) { @@ -373,8 +1049,17 @@ HB_FUNC( HB_SOCKETSEND ) if( lParam >= 0 && lParam < lLen ) lLen = lParam; } - hb_retnl( hb_socketSend( socket, hb_parc( 2 ), lLen, hb_parni( 4 ), - hb_parnintdef( 5, -1 ) ) ); + if( pSock->fRedirAll ) + { + int iAutoFlush = pSock->iAutoFlush; + if( iAutoFlush <= 0 ) + pSock->iAutoFlush = 15000; + lLen = hb_sockexWrite( pSock, hb_parc( 2 ), lLen, timeout ); + pSock->iAutoFlush = iAutoFlush; + } + else + lLen = hb_socketSend( pSock->sd, data, lLen, hb_parni( 4 ), timeout ); + hb_retnl( lLen ); } } @@ -384,7 +1069,7 @@ HB_FUNC( HB_SOCKETSENDTO ) void * addr; unsigned int len; - if( socket != HB_NO_SOCKET && socketaddrParam( 5, &addr, &len ) ) + if( socket != HB_NO_SOCKET && s_socketaddrParam( 5, &addr, &len ) ) { long lLen = ( long ) hb_parclen( 2 ); @@ -403,9 +1088,9 @@ HB_FUNC( HB_SOCKETSENDTO ) HB_FUNC( HB_SOCKETRECV ) { - HB_SOCKET socket = hb_socketParam( 1 ); + PHB_SOCKEX pSock = hb_sockexParam( 1 ); - if( socket != HB_NO_SOCKET ) + if( pSock ) { PHB_ITEM pItem = hb_param( 2, HB_IT_STRING ); char * pBuffer; @@ -419,11 +1104,14 @@ HB_FUNC( HB_SOCKETRECV ) if( lRead >= 0 && lRead < ( long ) nLen ) nLen = lRead; } - hb_retnl( hb_socketRecv( socket, pBuffer, ( long ) nLen, + hb_retnl( pSock->fRedirAll ? + hb_sockexRead( pSock, pBuffer, ( long ) nLen, + hb_parnintdef( 5, -1 ) ) : + hb_socketRecv( pSock->sd, pBuffer, ( long ) nLen, hb_parni( 4 ), hb_parnintdef( 5, -1 ) ) ); - return; } - hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } } @@ -571,18 +1259,18 @@ HB_FUNC( HB_SOCKETSETMULTICAST ) HB_FUNC( HB_SOCKETSELECTREAD ) { - HB_SOCKET socket = hb_socketParam( 1 ); + PHB_SOCKEX pSock = hb_sockexParam( 1 ); - if( socket != HB_NO_SOCKET ) - hb_retni( hb_socketSelectRead( socket, hb_parnintdef( 2, -1 ) ) ); + if( pSock ) + hb_retni( hb_sockexCanRead( pSock, HB_FALSE, hb_parnintdef( 2, -1 ) ) ); } HB_FUNC( HB_SOCKETSELECTWRITE ) { - HB_SOCKET socket = hb_socketParam( 1 ); + PHB_SOCKEX pSock = hb_sockexParam( 1 ); - if( socket != HB_NO_SOCKET ) - hb_retni( hb_socketSelectWrite( socket, hb_parnintdef( 2, -1 ) ) ); + if( pSock ) + hb_retni( hb_sockexCanWrite( pSock, HB_FALSE, hb_parnintdef( 2, -1 ) ) ); } HB_FUNC( HB_SOCKETSELECTWRITEEX ) @@ -593,24 +1281,13 @@ HB_FUNC( HB_SOCKETSELECTWRITEEX ) hb_retni( hb_socketSelectWriteEx( socket, hb_parnintdef( 2, -1 ) ) ); } -static HB_SOCKET socketSelectCallback( PHB_ITEM pItem ) -{ - HB_SOCKET socket = hb_socketItemGet( pItem ); - - if( socket != HB_NO_SOCKET ) - return socket; - - hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); - return HB_NO_SOCKET; -} - HB_FUNC( HB_SOCKETSELECT ) { s_socket_init(); - hb_retni( hb_socketSelect( hb_param( 1, HB_IT_ARRAY ), hb_parl( 2 ), + hb_retni( hb_sockexSelect( hb_param( 1, HB_IT_ARRAY ), hb_parl( 2 ), hb_param( 3, HB_IT_ARRAY ), hb_parl( 4 ), hb_param( 5, HB_IT_ARRAY ), hb_parl( 6 ), - hb_parnintdef( 7, -1 ), socketSelectCallback ) ); + hb_parnintdef( 7, -1 ), s_socketSelectCallback ) ); } HB_FUNC( HB_SOCKETRESOLVEINETADDR ) @@ -652,7 +1329,7 @@ HB_FUNC( HB_SOCKETGETHOSTNAME ) void * addr; unsigned int len; - if( socketaddrParam( 1, &addr, &len ) ) + if( s_socketaddrParam( 1, &addr, &len ) ) { char * szHostName = hb_socketGetHostName( addr, len ); @@ -722,3 +1399,87 @@ HB_FUNC( HB_SOCKETGETFD ) { hb_retnint( hb_socketParam( 1 ) ); } + +HB_FUNC( HB_SOCKETSETFILTER ) +{ + PHB_ITEM pItem = hb_param( 1, HB_IT_POINTER ); + + if( hb_sockexItemSetFilter( pItem, hb_parc( 2 ), hb_param( 3, HB_IT_ANY ) ) ) + hb_itemReturn( pItem ); + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_SOCKETGETFILTER ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + hb_retc_buffer( hb_sockexName( pSock ) ); +} + +HB_FUNC( HB_SOCKETREAD ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + { + PHB_ITEM pItem = hb_param( 2, HB_IT_STRING ); + char * pBuffer; + HB_SIZE nLen; + + if( pItem && HB_ISBYREF( 2 ) && hb_itemGetWriteCL( pItem, &pBuffer, &nLen ) ) + { + if( HB_ISNUM( 3 ) ) + { + long lRead = hb_parnl( 3 ); + if( lRead >= 0 && lRead < ( long ) nLen ) + nLen = lRead; + } + hb_retnl( hb_sockexRead( pSock, pBuffer, ( long ) nLen, + hb_parnintdef( 4, -1 ) ) ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + } +} + +HB_FUNC( HB_SOCKETWRITE ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + { + HB_MAXINT timeout = hb_parnintdef( 4, -1 ); + long lLen = ( long ) hb_parclen( 2 ); + + if( HB_ISNUM( 3 ) ) + { + long lWrite = hb_parnl( 3 ); + + if( lWrite >= 0 && lWrite < lLen ) + lLen = lWrite; + } + hb_retnl( hb_sockexWrite( pSock, hb_parc( 2 ), lLen, timeout ) ); + } +} + +HB_FUNC( HB_SOCKETFLUSH ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + hb_retnl( hb_sockexFlush( pSock, hb_parnintdef( 2, -1 ), hb_parl( 3 ) ) ); +} + +HB_FUNC( HB_SOCKETAUTOFLUSH ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + { + hb_retni( hb_sockexGetAutoFlush( pSock ) ); + if( HB_ISNUM( 2 ) ) + hb_sockexSetAutoFlush( pSock, hb_parni( 2 ) ); + } +} diff --git a/src/rtl/hbznet.c b/src/rtl/hbznet.c index 83a8fec3c4..75350387bc 100644 --- a/src/rtl/hbznet.c +++ b/src/rtl/hbznet.c @@ -46,11 +46,15 @@ * */ +/* this has to be declared before hbsocket.h is included */ +#define _HB_SOCKEX_IMPLEMENTATION_ +/* this has to be declared before hbznet.h is included */ #define _HB_ZNET_INTERNAL_ #include "hbznet.h" #include "hbbfish.h" #include "hbzlib.ch" +#include "hbinit.h" #include @@ -72,6 +76,7 @@ typedef struct _HB_ZNETSTREAM HB_ZNETSTREAM; #define HB_ZNET_BUFSIZE 0x4000 +#define HB_ZNET_READAHEAD 0x40 #if MAX_MEM_LEVEL >= 8 # define HB_ZNET_MEM_LEVEL 8 @@ -277,7 +282,8 @@ long hb_znetRead( PHB_ZNETSTREAM pStream, HB_SOCKET sd, void * buffer, long len, if( pStream->err != Z_OK && ! ( pStream->err == Z_BUF_ERROR && pStream->rd.avail_in == 0 ) ) { - hb_socketSetError( HB_SOCKET_ERR_OTHER ); + hb_socketSetError( HB_ZNET_SOCK_ERROR_BASE - pStream->err ); + rec = -1; break; } } @@ -357,12 +363,18 @@ static long hb_znetStreamWrite( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_MAXINT /* flush data in stream structure - return number of bytes left in the * buffer which were not sent */ -long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_MAXINT timeout ) +long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_MAXINT timeout, + HB_BOOL fSync ) { uInt uiSize = HB_ZNET_BUFSIZE - ( pStream->crypt ? 2 : 0 ); + if( fSync && pStream->wr.avail_out == uiSize && + pStream->wr.total_in == 0 && pStream->wr.total_out == 0 ) + return 0; + if( pStream->wr.avail_out > 0 ) - pStream->err = deflate( &pStream->wr, Z_PARTIAL_FLUSH ); + pStream->err = deflate( &pStream->wr, + fSync ? Z_FULL_FLUSH : Z_PARTIAL_FLUSH ); else pStream->err = Z_OK; @@ -372,9 +384,16 @@ long hb_znetFlush( PHB_ZNETSTREAM pStream, HB_SOCKET sd, HB_MAXINT timeout ) break; if( pStream->err == Z_OK ) - pStream->err = deflate( &pStream->wr, Z_PARTIAL_FLUSH ); + pStream->err = deflate( &pStream->wr, + fSync ? Z_FULL_FLUSH : Z_PARTIAL_FLUSH ); } + if( pStream->err == Z_BUF_ERROR ) + pStream->err = Z_OK; + + if( pStream->err != Z_OK ) + hb_socketSetError( HB_ZNET_SOCK_ERROR_BASE - pStream->err ); + return uiSize - pStream->wr.avail_out; } @@ -401,10 +420,298 @@ long hb_znetWrite( PHB_ZNETSTREAM pStream, HB_SOCKET sd, const void * buffer, lo } pStream->err = deflate( &pStream->wr, Z_NO_FLUSH ); if( pStream->err != Z_OK ) + { + if( pStream->err == Z_BUF_ERROR ) + pStream->err = Z_OK; + else + { + hb_socketSetError( HB_ZNET_SOCK_ERROR_BASE - pStream->err ); + snd = -1; + } break; + } } len -= pStream->wr.avail_in; return len == 0 ? snd : len; } + +/* socket filter */ + +#define HB_ZNET_GET( p ) ( ( PHB_ZNETSTREAM ) p->cargo ) + +static PHB_SOCKEX s_sockexNew( HB_SOCKET sd, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSock; + const void * keydata = NULL; + int keylen = 0, + level = HB_ZLIB_COMPRESSION_DEFAULT, + strategy = HB_ZLIB_STRATEGY_DEFAULT; + + hb_socekxParamsGetStd( pParams, &keydata, &keylen, NULL, NULL, + &level, &strategy ); + + pSock = hb_sockexNewZNet( sd, keydata, keylen, level, strategy ); + 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 ) + { + if( pSock->sd != HB_NO_SOCKET ) + hb_znetFlush( HB_ZNET_GET( pSock ), pSock->sd, + HB_MAX( 15000, pSock->iAutoFlush ), HB_TRUE ); + hb_znetClose( HB_ZNET_GET( pSock ) ); + } + + 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; + len -= lRead; + if( len == 0 || pSock->sd == HB_NO_SOCKET ) + return lRead; + data = ( HB_BYTE * ) data + lRead; + timeout = 0; + } + else if( pSock->sd == HB_NO_SOCKET ) + { + hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE ); + return -1; + } + + len = pSock->cargo ? hb_znetRead( HB_ZNET_GET( pSock ), pSock->sd, data, len, timeout ) : + hb_socketRecv( pSock->sd, data, len, 0, timeout ); + + return lRead > 0 ? HB_MAX( len, 0 ) + lRead : len; +} + +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 pSock->cargo ? hb_znetWrite( HB_ZNET_GET( pSock ), pSock->sd, data, len, timeout, NULL ) : + hb_socketSend( pSock->sd, data, len, 0, timeout ); +} + +static long s_sockexFlush( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync ) +{ + if( pSock->sd == HB_NO_SOCKET ) + { + hb_socketSetError( HB_SOCKET_ERR_INVALIDHANDLE ); + return -1; + } + return pSock->cargo ? hb_znetFlush( HB_ZNET_GET( pSock ), pSock->sd, timeout, fSync ) : 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( pSock->cargo ) + { + long len; + + if( pSock->buffer == NULL ) + { + if( pSock->readahead <= 0 ) + pSock->readahead = HB_ZNET_READAHEAD; + pSock->buffer = ( HB_BYTE * ) hb_xgrab( pSock->readahead ); + } + len = hb_znetRead( HB_ZNET_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; + } + else if( fBuffer && pSock->cargo && /* HB_ZNET_GET( pSock )->wr.avail_out > 0 && */ + ( uInt ) ( HB_ZNET_BUFSIZE - ( HB_ZNET_GET( pSock )->crypt ? 2 : 0 ) ) <= + HB_ZNET_GET( pSock )->wr.avail_out ) + return 1; + else + 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 ); + + switch( HB_ZNET_SOCK_ERROR_BASE - iError ) + { + case Z_STREAM_END: + return "Z_STREAM_END"; + case Z_NEED_DICT: + return "Z_NEED_DICT"; + case Z_ERRNO: + return "Z_ERRNO"; + case Z_STREAM_ERROR: + return "Z_STREAM_ERROR"; + case Z_DATA_ERROR: + return "Z_DATA_ERROR"; + case Z_MEM_ERROR: + return "Z_MEM_ERROR"; + case Z_BUF_ERROR: + return "Z_BUF_ERROR"; + case Z_VERSION_ERROR: + return "Z_VERSION_ERROR"; + } + + return hb_socketErrorStr( iError ); +} + +static const HB_SOCKET_FILTER s_sockFilter = +{ + "znet", + s_sockexNew, + s_sockexNext, + s_sockexClose, + s_sockexRead, + s_sockexWrite, + s_sockexFlush, + s_sockexCanRead, + s_sockexCanWrite, + s_sockexName, + s_sockexErrorStr +}; + +PHB_SOCKEX hb_sockexNewZNet( HB_SOCKET sd, const void * keydata, int keylen, + int level, int strategy ) +{ + PHB_SOCKEX pSock = NULL; + + if( sd != HB_NO_SOCKET ) + { + PHB_ZNETSTREAM pStream = NULL; + + if( level != HB_ZLIB_COMPRESSION_DISABLE ) + { + pStream = hb_znetOpen( level, strategy ); + if( pStream ) + { + if( keydata && keylen > 0 ) + hb_znetEncryptKey( pStream, keydata, keylen ); + } + else + sd = HB_NO_SOCKET; + } + if( sd != HB_NO_SOCKET ) + { + 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; +} + +/* hb_socketNewZNet( , [], ; + [], [] ) -> + */ +HB_FUNC( HB_SOCKETNEWZNET ) +{ + HB_SOCKET sd = hb_socketParam( 1 ); + + if( sd != HB_NO_SOCKET ) + { + PHB_SOCKEX pSock; + + if( HB_ISHASH( 2 ) ) + { + pSock = hb_sockexNew( sd, s_sockFilter.pszName, hb_param( 2, HB_IT_ANY ) ); + } + else + { + const char * keydata = hb_parc( 2 ); + int keylen = ( int ) hb_parclen( 2 ), + level = hb_parnidef( 3, HB_ZLIB_COMPRESSION_DEFAULT ), + strategy = hb_parnidef( 4, HB_ZLIB_STRATEGY_DEFAULT ); + + pSock = hb_sockexNewZNet( sd, keydata, keylen, level, strategy ); + } + + if( pSock ) + { + hb_socketItemClear( hb_param( 1, HB_IT_POINTER ) ); + hb_sockexItemPut( hb_param( -1, HB_IT_ANY ), pSock ); + } + } +} + +HB_CALL_ON_STARTUP_BEGIN( _hb_znet_init_ ) + hb_sockexRegister( &s_sockFilter ); +HB_CALL_ON_STARTUP_END( _hb_znet_init_ ) + +#if defined( HB_PRAGMA_STARTUP ) + #pragma startup _hb_znet_init_ +#elif defined( HB_DATASEG_STARTUP ) + #define HB_DATASEG_BODY HB_DATASEG_FUNC( _hb_znet_init_ ) + #include "hbiniseg.h" +#endif diff --git a/src/rtl/hbzsock.c b/src/rtl/hbzsock.c new file mode 100644 index 0000000000..9b05a13e6e --- /dev/null +++ b/src/rtl/hbzsock.c @@ -0,0 +1,575 @@ +/* + * Harbour Project source code: + * Harbour extended socket filter with ZLIB compression + * + * Copyright 2015 Przemyslaw Czerpak + * 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 "hbsocket.h" +#include "hbinit.h" +#include "hbzlib.ch" + +#include + +#define HB_ZSOCK_ERROR_BASE 100 + +#define HB_ZSOCK_READAHEAD 0x40 +#define HB_ZSOCK_RDBUFSIZE 4096 +#define HB_ZSOCK_WRBUFSIZE 4096 + +#if MAX_MEM_LEVEL >= 8 +# define HB_ZSOCK_MEM_LEVEL 8 +#else +# define HB_ZSOCK_MEM_LEVEL MAX_MEM_LEVEL +#endif + +#define HB_ZSOCK_GET( p ) ( ( PHB_SOCKEX_Z ) p->cargo ) + +typedef struct +{ + PHB_SOCKEX sock; + + HB_BOOL fDecompressIn; + HB_BOOL fCompressOut; + + z_stream z_read; + z_stream z_write; + + HB_BYTE * rdbuf; + HB_BYTE * wrbuf; +} +HB_SOCKEX_Z, * PHB_SOCKEX_Z; + +static voidpf s_zsock_zalloc( voidpf opaque, uInt items, uInt size ) +{ + HB_SYMBOL_UNUSED( opaque ); + return hb_xalloc( ( HB_SIZE ) items * size ); +} + +static void s_zsock_zfree( voidpf opaque, voidpf address ) +{ + HB_SYMBOL_UNUSED( opaque ); + hb_xfree( address ); +} + +static long s_zsock_write( PHB_SOCKEX_Z pZ, HB_MAXINT timeout ) +{ + long lSent = 0, len = HB_ZSOCK_WRBUFSIZE - pZ->z_write.avail_out; + + while( lSent < len ) + { + long l = hb_sockexWrite( pZ->sock, pZ->wrbuf + lSent, len - lSent, timeout ); + if( l <= 0 ) + { + switch( hb_socketGetError() ) + { + case HB_SOCKET_ERR_TIMEOUT: + case HB_SOCKET_ERR_AGAIN: + case HB_SOCKET_ERR_TRYAGAIN: + break; + default: + lSent = -1; + break; + } + break; + } + + lSent += l; + if( timeout > 0 ) + timeout = 0; + } + + if( lSent > 0 ) + { + if( lSent < len ) + memmove( pZ->wrbuf, pZ->wrbuf + lSent, len - lSent ); + pZ->z_write.avail_out += lSent; + pZ->z_write.next_out -= lSent; + } + + return lSent; +} + +static int s_zsock_inbuffer( PHB_SOCKEX pSock ) +{ + PHB_SOCKEX_Z pZ = HB_ZSOCK_GET( pSock ); + + if( pSock->inbuffer == 0 && pZ->fDecompressIn ) + { + int err; + + if( pSock->buffer == NULL ) + { + if( pSock->readahead <= 0 ) + pSock->readahead = HB_ZSOCK_READAHEAD; + pSock->buffer = ( HB_BYTE * ) hb_xgrab( pSock->readahead ); + } + + pZ->z_read.next_out = ( Bytef * ) pSock->buffer; + pZ->z_read.avail_out = ( uInt ) pSock->readahead; + + err = inflate( &pZ->z_read, Z_SYNC_FLUSH ); + if( err != Z_OK && err != Z_BUF_ERROR ) + hb_socketSetError( HB_ZSOCK_ERROR_BASE - err ); + pSock->inbuffer = pSock->readahead - pZ->z_read.avail_out; + } + return pSock->inbuffer > 0 ? 1 : 0; +} + + +/* socket filter */ + +static long s_sockexRead( PHB_SOCKEX pSock, void * data, long len, HB_MAXINT timeout ) +{ + PHB_SOCKEX_Z pZ = HB_ZSOCK_GET( pSock ); + long lRecv = 0; + + if( pSock->inbuffer > 0 && len > 0 ) + { + lRecv = HB_MIN( pSock->inbuffer, len ); + memcpy( data, pSock->buffer + pSock->posbuffer, lRecv ); + if( ( pSock->inbuffer -= lRecv ) > 0 ) + pSock->posbuffer += lRecv; + else + pSock->posbuffer = 0; + return lRecv; + } + else if( pZ->fDecompressIn ) + { + int err = Z_OK; + + pZ->z_read.next_out = ( Bytef * ) data; + pZ->z_read.avail_out = ( uInt ) len; + pZ->z_read.total_out = 0; + + while( pZ->z_read.avail_out ) + { + if( err == Z_BUF_ERROR && pZ->z_read.avail_in == 0 ) + { + lRecv = hb_sockexRead( pZ->sock, pZ->rdbuf, HB_ZSOCK_RDBUFSIZE, + pZ->z_read.total_out == 0 ? timeout : 0 ); + if( lRecv <= 0 ) + break; + pZ->z_read.next_in = ( Bytef * ) pZ->rdbuf; + pZ->z_read.avail_in = ( uInt ) lRecv; + } + else if( err != Z_OK ) + { + hb_socketSetError( HB_ZSOCK_ERROR_BASE - err ); + lRecv = -1; + break; + } + err = inflate( &pZ->z_read, Z_SYNC_FLUSH ); + } + + if( pZ->z_read.total_out != 0 ) + lRecv = ( long ) pZ->z_read.total_out; + + return pZ->z_read.total_out != 0 ? ( long ) pZ->z_read.total_out : lRecv; + } + else + return hb_sockexRead( pZ->sock, data, len, timeout ); +} + +static long s_sockexWrite( PHB_SOCKEX pSock, const void * data, long len, HB_MAXINT timeout ) +{ + PHB_SOCKEX_Z pZ = HB_ZSOCK_GET( pSock ); + + if( pZ->fCompressOut ) + { + long lWritten = 0; + + pZ->z_write.next_in = ( Bytef * ) data; + pZ->z_write.avail_in = ( uInt ) len; + + while( pZ->z_write.avail_in ) + { + int err; + + if( pZ->z_write.avail_out == 0 ) + { + lWritten = s_zsock_write( pZ, timeout ); + if( lWritten <= 0 ) + break; + timeout = 0; + } + err = deflate( &pZ->z_write, Z_NO_FLUSH ); + if( err != Z_OK ) + { + if( err != Z_BUF_ERROR ) + { + hb_socketSetError( HB_ZSOCK_ERROR_BASE - err ); + lWritten = -1; + } + break; + } + } + + return lWritten >= 0 ? len - pZ->z_write.avail_in : lWritten; + } + else + return hb_sockexWrite( pSock, data, len, timeout ); +} + +static long s_sockexFlush( PHB_SOCKEX pSock, HB_MAXINT timeout, HB_BOOL fSync ) +{ + PHB_SOCKEX_Z pZ = HB_ZSOCK_GET( pSock ); + long lResult = 0; + + if( pZ->fCompressOut && + ( ! fSync || pZ->z_write.avail_out != HB_ZSOCK_WRBUFSIZE || + pZ->z_write.total_in != 0 || pZ->z_write.total_out != 0 ) ) + { + int err; + + if( pZ->z_write.avail_out > 0 ) + err = deflate( &pZ->z_write, fSync ? Z_FULL_FLUSH : Z_PARTIAL_FLUSH ); + else + err = Z_OK; + + while( pZ->z_write.avail_out < HB_ZSOCK_WRBUFSIZE ) + { + if( s_zsock_write( pZ, timeout ) <= 0 ) + break; + if( err == Z_OK ) + err = deflate( &pZ->z_write, fSync ? Z_FULL_FLUSH : Z_PARTIAL_FLUSH ); + } + if( err != Z_OK && err != Z_BUF_ERROR ) + hb_socketSetError( HB_ZSOCK_ERROR_BASE - err ); + lResult = HB_ZSOCK_WRBUFSIZE - pZ->z_write.avail_out; + } + return lResult + hb_sockexFlush( HB_ZSOCK_GET( pSock )->sock, timeout, fSync ); +} + +static int s_sockexCanRead( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) +{ + return s_zsock_inbuffer( pSock ) ? 1 : + hb_sockexCanRead( HB_ZSOCK_GET( pSock )->sock, fBuffer, timeout ); +} + +static int s_sockexCanWrite( PHB_SOCKEX pSock, HB_BOOL fBuffer, HB_MAXINT timeout ) +{ + return hb_sockexCanWrite( HB_ZSOCK_GET( pSock )->sock, fBuffer, timeout ); +} + +static char * s_sockexName( PHB_SOCKEX pSock ) +{ + char * pszName = hb_sockexIsRaw( HB_ZSOCK_GET( pSock )->sock ) ? NULL : + hb_sockexName( HB_ZSOCK_GET( pSock )->sock ); + if( pszName ) + { + char * pszFree = pszName; + pszName = hb_xstrcpy( NULL, pSock->pFilter->pszName, "|", pszName, NULL ); + hb_xfree( pszFree ); + } + else + pszName = hb_strdup( pSock->pFilter->pszName ); + + return pszName; +} + +static const char * s_sockexErrorStr( PHB_SOCKEX pSock, int iError ) +{ + switch( HB_ZSOCK_ERROR_BASE - iError ) + { + case Z_STREAM_END: + return "Z_STREAM_END"; + case Z_NEED_DICT: + return "Z_NEED_DICT"; + case Z_ERRNO: + return "Z_ERRNO"; + case Z_STREAM_ERROR: + return "Z_STREAM_ERROR"; + case Z_DATA_ERROR: + return "Z_DATA_ERROR"; + case Z_MEM_ERROR: + return "Z_MEM_ERROR"; + case Z_BUF_ERROR: + return "Z_BUF_ERROR"; + case Z_VERSION_ERROR: + return "Z_VERSION_ERROR"; + } + + return hb_sockexErrorStr( HB_ZSOCK_GET( pSock )->sock, iError ); +} + +static int s_sockexClose( PHB_SOCKEX pSock, HB_BOOL fClose ) +{ + PHB_SOCKEX_Z pZ = HB_ZSOCK_GET( pSock ); + int iResult = 0; + + if( pZ ) + { + if( pZ->sock ) + s_sockexFlush( pSock, HB_MAX( 15000, pSock->iAutoFlush ), HB_TRUE ); + + if( pZ->fDecompressIn ) + inflateEnd( &pZ->z_read ); + if( pZ->rdbuf ) + hb_xfree( pZ->rdbuf ); + if( pZ->fCompressOut ) + deflateEnd( &pZ->z_write ); + if( pZ->wrbuf ) + hb_xfree( pZ->wrbuf ); + + if( pZ->sock ) + { + if( pSock->fShutDown ) + pZ->sock->fShutDown = HB_TRUE; + if( pSock->iAutoFlush != 0 && pZ->sock->iAutoFlush == 0 ) + pZ->sock->iAutoFlush = pSock->iAutoFlush; + iResult = hb_sockexClose( pZ->sock, fClose ); + } + + hb_xfree( pZ ); + } + /* call hb_sockexRawClear() with fClose = HB_FALSE because + hb_sockexClose() already closed real socket */ + hb_sockexRawClear( pSock, HB_FALSE ); + hb_xfree( pSock ); + + return iResult; +} + +static PHB_SOCKEX s_sockexNext( PHB_SOCKEX pSock, PHB_ITEM pParams ); + +static PHB_SOCKEX s_sockexNew( HB_SOCKET sd, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSock, pSockNew = NULL; + + pSock = hb_sockexNew( sd, NULL, pParams ); + if( pSock ) + { + pSockNew = s_sockexNext( pSock, pParams ); + if( pSockNew == NULL ) + hb_sockexClose( pSock, HB_FALSE ); + } + + return pSockNew; +} + +static const HB_SOCKET_FILTER s_sockFilter = +{ + "ZSOCK", + s_sockexNew, + s_sockexNext, + s_sockexClose, + s_sockexRead, + s_sockexWrite, + s_sockexFlush, + s_sockexCanRead, + s_sockexCanWrite, + s_sockexName, + s_sockexErrorStr +}; + +static PHB_SOCKEX s_sockexNext( PHB_SOCKEX pSock, PHB_ITEM pParams ) +{ + PHB_SOCKEX pSockNew = NULL; + + if( pSock ) + { + HB_BOOL fDecompressIn = HB_TRUE, fCompressOut = HB_TRUE; + int level = HB_ZLIB_COMPRESSION_DEFAULT, + strategy = HB_ZLIB_STRATEGY_DEFAULT, + windowBitsIn = MAX_WBITS, windowBitsOut = MAX_WBITS; + + if( pParams && HB_IS_HASH( pParams ) ) + { + PHB_ITEM pItem; + + if( ( pItem = hb_hashGetCItemPtr( pParams, "zlib" ) ) != NULL && + HB_IS_NUMERIC( pItem ) ) + level = hb_itemGetNI( pItem ); + if( ( pItem = hb_hashGetCItemPtr( pParams, "zs" ) ) != NULL && + HB_IS_NUMERIC( pItem ) ) + strategy = hb_itemGetNI( pItem ); + + if( ( pItem = hb_hashGetCItemPtr( pParams, "gzin" ) ) != NULL && + HB_IS_LOGICAL( pItem ) ) + { + fDecompressIn = hb_itemGetL( pItem ); + if( fDecompressIn ) + windowBitsIn += 16; + } + if( ( pItem = hb_hashGetCItemPtr( pParams, "zin" ) ) != NULL && + HB_IS_LOGICAL( pItem ) ) + { + if( windowBitsIn == MAX_WBITS ) + fDecompressIn = hb_itemGetL( pItem ); + else if( hb_itemGetL( pItem ) ) + windowBitsIn += 16; + } + + if( ( pItem = hb_hashGetCItemPtr( pParams, "gzout" ) ) != NULL && + HB_IS_LOGICAL( pItem ) ) + { + fCompressOut = hb_itemGetL( pItem ); + if( fCompressOut ) + windowBitsOut += 16; + } + if( ( pItem = hb_hashGetCItemPtr( pParams, "zout" ) ) != NULL && + HB_IS_LOGICAL( pItem ) && windowBitsOut == MAX_WBITS ) + fCompressOut = hb_itemGetL( pItem ); + } + + if( level != HB_ZLIB_COMPRESSION_DISABLE && + ( fDecompressIn || fCompressOut ) ) + { + PHB_SOCKEX_Z pZ = ( PHB_SOCKEX_Z ) hb_xgrabz( sizeof( HB_SOCKEX_Z ) ); + + pSockNew = ( PHB_SOCKEX ) hb_xgrabz( sizeof( HB_SOCKEX ) ); + pSockNew->sd = HB_NO_SOCKET; + pSockNew->fRedirAll = HB_TRUE; + pSockNew->pFilter = &s_sockFilter; + + pSockNew->cargo = ( void * ) pZ; + pZ->z_read.zalloc = s_zsock_zalloc; + pZ->z_read.zfree = s_zsock_zfree; + pZ->z_read.opaque = Z_NULL; + + pZ->z_write.zalloc = s_zsock_zalloc; + pZ->z_write.zfree = s_zsock_zfree; + pZ->z_write.opaque = Z_NULL; + + pZ->z_read.next_in = NULL; + pZ->z_read.avail_in = 0; + + if( level != Z_DEFAULT_COMPRESSION && + !( level >= Z_NO_COMPRESSION && level <= Z_BEST_COMPRESSION ) ) + level = Z_DEFAULT_COMPRESSION; + + if( strategy != Z_FILTERED && +#if defined( Z_RLE ) + strategy != Z_RLE && +#endif +#if defined( Z_FIXED ) + strategy != Z_FIXED && +#endif + strategy != Z_HUFFMAN_ONLY ) + strategy = Z_DEFAULT_STRATEGY; + + if( fDecompressIn && level != HB_ZLIB_COMPRESSION_DISABLE ) + { + /* MAX_WBITS=15, decompression - support for formats: + * -15: raw, 15: ZLIB, 31: GZIP, 47: ZLIB+GZIP + */ + if( inflateInit2( &pZ->z_read, windowBitsIn ) == Z_OK ) + { + pZ->fDecompressIn = HB_TRUE; + pZ->rdbuf = ( HB_BYTE * ) hb_xgrab( HB_ZSOCK_RDBUFSIZE ); + } + else + level = HB_ZLIB_COMPRESSION_DISABLE; + } + + if( fCompressOut && level != HB_ZLIB_COMPRESSION_DISABLE ) + { + /* MAX_WBITS=15, compression format: + * -15: raw, 15: ZLIB (+6 bytes), 31: GZIP(+18 bytes) + */ + if( deflateInit2( &pZ->z_write, level, + Z_DEFLATED, windowBitsOut, HB_ZSOCK_MEM_LEVEL, + strategy ) == Z_OK ) + { + pZ->fCompressOut = HB_TRUE; + pZ->wrbuf = ( HB_BYTE * ) hb_xgrab( HB_ZSOCK_WRBUFSIZE ); + pZ->z_write.next_out = ( Bytef * ) pZ->wrbuf; + pZ->z_write.avail_out = HB_ZSOCK_WRBUFSIZE; + } + else + level = HB_ZLIB_COMPRESSION_DISABLE; + } + + if( level != HB_ZLIB_COMPRESSION_DISABLE ) + { + pSockNew->sd = pSock->sd; + pSockNew->fShutDown = pSock->fShutDown; + pSockNew->iAutoFlush = pSock->iAutoFlush; + pZ->sock = pSock; + hb_socekxParamsInit( pSockNew, pParams ); + } + else + { + s_sockexClose( pSockNew, HB_FALSE ); + pSockNew = NULL; + } + } + } + + return pSockNew; +} + +/* hb_socketNewZSock( , [] ) -> */ +HB_FUNC( HB_SOCKETNEWZSOCK ) +{ + PHB_SOCKEX pSock = hb_sockexParam( 1 ); + + if( pSock ) + { + pSock = s_sockexNext( pSock, hb_param( 2, HB_IT_HASH ) ); + if( pSock ) + { + hb_sockexItemClear( hb_param( 1, HB_IT_POINTER ) ); + hb_sockexItemPut( hb_param( -1, HB_IT_ANY ), pSock ); + } + } +} + + +HB_CALL_ON_STARTUP_BEGIN( _hb_zsock_init_ ) + hb_sockexRegister( &s_sockFilter ); +HB_CALL_ON_STARTUP_END( _hb_zsock_init_ ) + +#if defined( HB_PRAGMA_STARTUP ) + #pragma startup _hb_zsock_init_ +#elif defined( HB_DATASEG_STARTUP ) + #define HB_DATASEG_BODY HB_DATASEG_FUNC( _hb_zsock_init_ ) + #include "hbiniseg.h" +#endif