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