diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 647aaa20b5..9c6eea4000 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -17,6 +17,81 @@ past entries belonging to author(s): Viktor Szakats. */ +2010-01-06 17:15 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/src/rtl/hbznet.c + ! do not use DEF_MEM_LEVEL to avoid potential problems when + is not available + + * harbour/contrib/hbnetio/netio.h + * harbour/contrib/hbnetio/netiocli.c + * harbour/contrib/hbnetio/netiosrv.c + + implemented RPC in HBNETIO protocol + + added the following client functions: + check if function/procedure exists on the server side: + NETIO_PROCEXISTS( ) -> + execute function/procedure on server the side, + do not wait for confirmation: + NETIO_PROCEXEC( [, ] ) -> + execute function/procedure on the server side and wait for + confirmation: + NETIO_PROCEXECW( [, ] ) -> + execute function on the server side and wait for its return value: + NETIO_FUNCEXEC( [, ] ) -> + All above functions use default connection set by NETIO_CONNECT() + for RPCs but it's also possible to specify server address and port + in / just like in parameter in RDD + functions, i.e.: + NETIO_PROCEXISTS( "192.168.0.1:10005:MYFUNC" ) + + added new server side functions to enable/disable/check RPC support: + NETIO_RPC( | [, ] ) + -> + if RPC is enabled for listen socket then connection sockets inherit + this setting. + + added 4-th parameter to NETIO_LISTEN() function. .T. enable + RPC support in returned listen socket which is later inherited by + connection sockets + * changed protocol version ID - current NETIO clients and servers + cannot be used with old code + + * harbour/contrib/hbnetio/utils/netiosrv.prg + * added option to enable RPC support in NETIO server + + If you want to make some test then you can execute netiosrv with + non empty 4-th parameter as server, i.e.: + ./netiosrv "" "" "" 1 + and try this code as client: + + proc main() + // pass server address to netio_connect() for non localhost tests + ? "NETIO_CONNECT():", netio_connect() + ? + ? "DATE() function is supported:", netio_procexists( "DATE" ) + ? "QOUT() function is supported:", netio_procexists( "DATE" ) + ? "HB_DATETIME() function is supported:", ; + netio_procexists( "HB_DATETIME" ) + ? + ? netio_procexec( "QOUT", repl( "=", 50 ) ) + ? netio_procexec( "QOUT", "This is RPC TEST", date(), hb_datetime() ) + ? netio_procexecw( "QOUT", repl( "=", 50 ) ) + ? + ? "SERVER DATE:", netio_funcexec( "DATE" ) + ? "SERVER TIME:", netio_funcexec( "TIME" ) + ? "SERVER DATETIME:", netio_funcexec( "HB_DATETIME" ) + ? + ? netio_funcexec( "upper", "hello world !!!" ) + return + + Please remember that only functions linked with server are available. + If you want to enabled all core functions in netiosrv then please + uncomment this line in netiosrv.prg: + REQUEST __HB_EXTERN__ + + + Have a fun with a new toy. I hope that many Harbour user will find it + very interesting. Please only be careful !!!. This feature allows to + execute remotely _ANY_ code on the server side. _NEVER_ leave open ports + with RPC support for untrusted access. + 2010-01-06 00:21 UTC-0800 Pritpal Bedi (pritpal@vouchcac.com) * contrib/hbide/hbide.prg * contrib/hbide/idedocks.prg diff --git a/harbour/contrib/hbnetio/netio.h b/harbour/contrib/hbnetio/netio.h index 64b9d7af62..8242e212d8 100644 --- a/harbour/contrib/hbnetio/netio.h +++ b/harbour/contrib/hbnetio/netio.h @@ -73,7 +73,7 @@ #define NETIO_SERVERNAME_MAX 256 /* login string */ -#define NETIO_LOGINSTRID "HarbourFileTcpIpServer\005" +#define NETIO_LOGINSTRID "HarbourFileTcpIpServer\006" /* messages */ #define NETIO_LOGIN 1 @@ -91,6 +91,10 @@ #define NETIO_CLOSE 13 #define NETIO_ERROR 14 #define NETIO_SYNC 15 +#define NETIO_PROCIS 16 +#define NETIO_PROC 17 +#define NETIO_PROCW 18 +#define NETIO_FUNC 19 #define NETIO_CONNECTED 0x4321DEAD /* messages format */ @@ -104,9 +108,13 @@ /* { NETIO_LOCK, file_no[2], start[ 8 ], len[ 8 ], flags[ 2 ], ... } -> { NETIO_LOCK, ... } */ /* { NETIO_TRUNC, file_no[2], offset[ 8 ], ... } -> { NETIO_TRUNC, ... } */ /* { NETIO_SIZE, file_no[2], ... } -> { NETIO_SIZE, size[ 8 ], err[ 4 ], ... } */ -/* { NETIO_COMMIT, file_no[2], ... } -> { NETIO_SYNC, ... } */ +/* { NETIO_COMMIT, file_no[2], ... } -> { NETIO_SYNC, ... } | NULL */ /* { NETIO_CLOSE, file_no[2], ... } -> { NETIO_CLOSE, ... } */ -/* { NETIO_UNLOCK, file_no[2], start[ 8 ], len[ 8 ], flags[ 2 ], ... } -> { NETIO_SYNC, ... } */ +/* { NETIO_UNLOCK, file_no[2], start[ 8 ], len[ 8 ], flags[ 2 ], ... } -> { NETIO_SYNC, ... } | NULL */ +/* { NETIO_PROCIS, size[ 4 ] } + (funcname + \0 + data)[ size ] -> { NETIO_PROCIS, ... } */ +/* { NETIO_PROC, size[ 4 ] } + (funcname + \0 + data)[ size ] -> { NETIO_SYNC, ... } | NULL */ +/* { NETIO_PROCW, size[ 4 ] } + (funcname + \0 + data)[ size ] -> { NETIO_PROC, ... } */ +/* { NETIO_FUNC, size[ 4 ] } + (funcname + \0 + data)[ size ] -> { NETIO_FUNC, size[ 4 ] } + data[ size ] */ /* { NETIO_SYNC, ... } -> NULL */ /* alternative answer for all messages: -> { NETIO_ERROR, err[ 4 ], ... } */ @@ -118,3 +126,5 @@ #define NETIO_ERR_FILES_MAX 0xff05 #define NETIO_ERR_READ 0xff06 #define NETIO_ERR_FILE_IO 0xff07 +#define NETIO_ERR_NOT_EXISTS 0xff08 +#define NETIO_ERR_UNSUPPORTED 0xff09 diff --git a/harbour/contrib/hbnetio/netiocli.c b/harbour/contrib/hbnetio/netiocli.c index ef6928f5b5..f3f30a9f3c 100644 --- a/harbour/contrib/hbnetio/netiocli.c +++ b/harbour/contrib/hbnetio/netiocli.c @@ -468,6 +468,8 @@ static void s_netio_init( void ) } } +/* NETIO_CONNECT( [], [], [] ) -> + */ HB_FUNC( NETIO_CONNECT ) { const char * pszServer = hb_parc( 1 ); @@ -491,6 +493,133 @@ HB_FUNC( NETIO_CONNECT ) hb_retl( conn != NULL ); } +static const char * s_netio_params( int iMsg, const char * pszName, UINT32 * pSize, char ** pFree ) +{ + int iPCount = iMsg == NETIO_PROCIS ? 0 : hb_pcount(), i; + char * data = NULL, * itmData; + ULONG size = 0, itmSize; + + size = ( ULONG ) strlen( pszName ) + 1; + + for( i = 2; i <= iPCount; ++i ) + { + itmData = hb_itemSerialize( hb_param( i, HB_IT_ANY ), TRUE, &itmSize ); + if( data == NULL ) + data = ( char * ) memcpy( hb_xgrab( size + itmSize ), pszName, size ); + else + data = ( char * ) hb_xrealloc( data, size + itmSize ); + memcpy( data + size, itmData, itmSize ); + size += itmSize; + hb_xfree( itmData ); + } + + *pFree = data; + *pSize = ( UINT32 ) size; + + return data ? data : pszName; +} + +static BOOL s_netio_procexec( const char * pszProcName, int iMsg ) +{ + BOOL fResult = FALSE; + + if( pszProcName ) + { + PHB_CONCLI conn = s_fileConnect( &pszProcName, NULL, 0, 0 ); + if( conn ) + { + if( s_fileConLock( conn ) ) + { + BYTE msgbuf[ NETIO_MSGLEN ]; + const char * data; + char * buffer; + UINT32 size; + + data = s_netio_params( iMsg, pszProcName, &size, &buffer ); + HB_PUT_LE_UINT32( &msgbuf[ 0 ], iMsg ); + HB_PUT_LE_UINT32( &msgbuf[ 4 ], size ); + memset( msgbuf + 8, '\0', sizeof( msgbuf ) - 8 ); + fResult = s_fileSendMsg( conn, msgbuf, data, size, iMsg != NETIO_PROC ); + if( fResult && iMsg == NETIO_FUNC ) + { + ULONG ulResult = HB_GET_LE_UINT32( &msgbuf[ 4 ] ); + + if( ulResult > 0 ) + { + PHB_ITEM pItem; + + if( ulResult > size && buffer ) + { + hb_xfree( buffer ); + buffer = NULL; + } + if( buffer == NULL ) + buffer = ( char * ) hb_xgrab( ulResult ); + ulResult = s_fileRecvAll( conn, buffer, ulResult ); + data = buffer; + pItem = hb_itemDeserialize( &data, &ulResult ); + if( pItem ) + hb_itemReturnRelease( pItem ); + /* else TODO: RTE */ + } + } + if( buffer ) + hb_xfree( buffer ); + s_fileConUnlock( conn ); + } + s_fileConClose( conn ); + } + } + + return fResult; +} + +/* check if function/procedure exists on the server side: + * + * NETIO_PROCEXISTS( ) -> + */ +HB_FUNC( NETIO_PROCEXISTS ) +{ + const char * pszProcName = hb_parc( 1 ); + + hb_retl( s_netio_procexec( pszProcName, NETIO_PROCIS ) ); +} + +/* execute function/procedure on server the side, + * do not wait for confirmation: + * + * NETIO_PROCEXEC( [, ] ) -> + */ +HB_FUNC( NETIO_PROCEXEC ) +{ + const char * pszProcName = hb_parc( 1 ); + + hb_retl( s_netio_procexec( pszProcName, NETIO_PROC ) ); +} + +/* execute function/procedure on the server side and wait for + * confirmation: + * + * NETIO_PROCEXECW( [, ] ) -> + */ +HB_FUNC( NETIO_PROCEXECW ) +{ + const char * pszProcName = hb_parc( 1 ); + + hb_retl( s_netio_procexec( pszProcName, NETIO_PROCW ) ); +} + +/* execute function on the server side and wait for its return value: + * + * NETIO_FUNCEXEC( [, ] ) -> + */ +HB_FUNC( NETIO_FUNCEXEC ) +{ + const char * pszProcName = hb_parc( 1 ); + + s_netio_procexec( pszProcName, NETIO_FUNC ); +} + /* Client methods */ static BOOL s_fileAccept( const char * pFilename ) diff --git a/harbour/contrib/hbnetio/netiosrv.c b/harbour/contrib/hbnetio/netiosrv.c index 5b974b6d36..f6d87ccedb 100644 --- a/harbour/contrib/hbnetio/netiosrv.c +++ b/harbour/contrib/hbnetio/netiosrv.c @@ -61,11 +61,13 @@ */ #include "hbapi.h" +#include "hbapiitm.h" #include "hbapifs.h" #include "hbapierr.h" #include "hbsocket.h" #include "hbinit.h" #include "hbvm.h" +#include "hbstack.h" #include "hbthread.h" #include "netio.h" @@ -81,6 +83,7 @@ typedef struct _HB_CONSRV int filesCount; int firstFree; BOOL stop; + BOOL rpc; int rootPathLen; char rootPath[ HB_PATH_MAX ]; } @@ -90,6 +93,7 @@ typedef struct _HB_LISTENSD { HB_SOCKET sd; BOOL stop; + BOOL rpc; char rootPath[ HB_PATH_MAX ]; } HB_LISTENSD, * PHB_LISTENSD; @@ -263,11 +267,12 @@ static void s_consrvRet( PHB_CONSRV conn ) hb_ret(); } -static PHB_CONSRV s_consrvNew( HB_SOCKET connsd, const char * szRootPath ) +static PHB_CONSRV s_consrvNew( HB_SOCKET connsd, const char * szRootPath, BOOL rpc ) { PHB_CONSRV conn = ( PHB_CONSRV ) memset( hb_xgrab( sizeof( HB_CONSRV ) ), 0, sizeof( HB_CONSRV ) ); conn->sd = connsd; + conn->rpc = rpc; if( szRootPath ) { hb_strncpy( conn->rootPath, szRootPath, sizeof( conn->rootPath ) - 1 ); @@ -355,7 +360,7 @@ static PHB_LISTENSD s_listenParam( int iParam, BOOL fError ) return NULL; } -static void s_listenRet( HB_SOCKET sd, const char * szRootPath ) +static void s_listenRet( HB_SOCKET sd, const char * szRootPath, BOOL rpc ) { if( sd != HB_NO_SOCKET ) { @@ -365,6 +370,7 @@ static void s_listenRet( HB_SOCKET sd, const char * szRootPath ) lsd = ( PHB_LISTENSD ) memset( hb_xgrab( sizeof( HB_LISTENSD ) ), 0, sizeof( HB_LISTENSD ) ); lsd->sd = sd; + lsd->rpc = rpc; if( szRootPath ) hb_strncpy( lsd->rootPath, szRootPath, sizeof( lsd->rootPath ) - 1 ); else @@ -389,6 +395,29 @@ static void s_listenRet( HB_SOCKET sd, const char * szRootPath ) } +HB_FUNC( NETIO_RPC ) +{ + PHB_LISTENSD lsd = s_listenParam( 1, FALSE ); + BOOL fRPC = FALSE; + + if( lsd ) + { + fRPC = lsd->rpc; + if( HB_ISLOG( 2 ) ) + lsd->rpc = hb_parl( 2 ); + } + else + { + PHB_CONSRV conn = s_consrvParam( 1 ); + if( conn ) + { + fRPC = conn->rpc; + if( HB_ISLOG( 2 ) ) + conn->rpc = hb_parl( 2 ); + } + } +} + HB_FUNC( NETIO_SERVERSTOP ) { PHB_LISTENSD lsd = s_listenParam( 1, FALSE ); @@ -400,7 +429,7 @@ HB_FUNC( NETIO_SERVERSTOP ) { PHB_CONSRV conn = s_consrvParam( 1 ); if( conn ) - lsd->stop = fStop; + conn->stop = fStop; } } @@ -411,6 +440,7 @@ HB_FUNC( NETIO_LISTEN ) int iPort = hb_parnidef( 1, NETIO_DEFAULT_PORT ); const char * szAddress = hb_parc( 2 ); const char * szRootPath = hb_parc( 3 ); + BOOL fRPC = hb_parl( 4 ); void * pSockAddr; unsigned uiLen; HB_SOCKET sd = HB_NO_SOCKET; @@ -436,7 +466,7 @@ HB_FUNC( NETIO_LISTEN ) hb_xfree( pSockAddr ); } - s_listenRet( sd, szRootPath ); + s_listenRet( sd, szRootPath, fRPC ); } HB_FUNC( NETIO_ACCEPT ) @@ -461,7 +491,7 @@ HB_FUNC( NETIO_ACCEPT ) BYTE msgbuf[ NETIO_MSGLEN * 2 ]; hb_socketSetNoDelay( connsd, TRUE ); - conn = s_consrvNew( connsd, lsd->rootPath ); + conn = s_consrvNew( connsd, lsd->rootPath, lsd->rpc ); if( s_srvRecvAll( conn, msgbuf, NETIO_MSGLEN ) == NETIO_MSGLEN && HB_GET_LE_INT32( msgbuf ) == NETIO_LOGIN ) @@ -507,6 +537,7 @@ HB_FUNC( NETIO_SERVER ) HB_ERRCODE errCode = 0, errFsCode; long len = 0, size, size2; int iFileNo; + UINT32 uiMsg; USHORT uiFalgs; char * szExt; PHB_FILE pFile; @@ -517,7 +548,8 @@ HB_FUNC( NETIO_SERVER ) if( s_srvRecvAll( conn, msgbuf, NETIO_MSGLEN ) != NETIO_MSGLEN ) break; - switch( HB_GET_LE_UINT32( msgbuf ) ) + uiMsg = HB_GET_LE_UINT32( msgbuf ); + switch( uiMsg ) { case NETIO_EXISTS: size = HB_GET_LE_UINT16( &msgbuf[ 4 ] ); @@ -737,7 +769,6 @@ HB_FUNC( NETIO_SERVER ) errCode = s_srvFsError(); else if( !fNoAnswer ) { - UINT32 uiMsg = HB_GET_LE_UINT32( msgbuf ); HB_PUT_LE_UINT32( &msg[ 0 ], uiMsg ); memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); } @@ -795,6 +826,95 @@ HB_FUNC( NETIO_SERVER ) fNoAnswer = TRUE; break; + case NETIO_PROC: + fNoAnswer = TRUE; + case NETIO_PROCIS: + case NETIO_PROCW: + case NETIO_FUNC: + if( !conn->rpc ) + { + errCode = NETIO_ERR_UNSUPPORTED; + break; + } + size = HB_GET_LE_UINT32( &msgbuf[ 4 ] ); + if( size < 2 ) + errCode = NETIO_ERR_WRONG_PARAM; + else + { + if( size >= ( long ) sizeof( buffer ) ) + ptr = msg = ( BYTE * ) hb_xgrab( size ); + if( s_srvRecvAll( conn, msg, size ) != size ) + errCode = NETIO_ERR_READ; + else + { + const char * data = ( const char * ) msg; + size2 = ( long ) hb_strnlen( data, size ) + 1; + if( size2 > size ) + errCode = NETIO_ERR_WRONG_PARAM; + else + { + PHB_DYNS pDynSym = hb_dynsymFindName( data ); + if( !pDynSym || !hb_dynsymIsFunction( pDynSym ) ) + errCode = NETIO_ERR_NOT_EXISTS; + else if( uiMsg != NETIO_PROCIS ) + { + ULONG ulSize = size - size2; + USHORT uiPCount = 0; + + data += size2; + + hb_vmPushDynSym( pDynSym ); + hb_vmPushNil(); + while( ulSize ) + { + PHB_ITEM pItem = hb_itemDeserialize( &data, &ulSize ); + if( !pItem ) + { + ulSize = 1; + break; + } + ++uiPCount; + hb_vmPush( pItem ); + hb_itemRelease( pItem ); + } + if( ulSize ) + { + errCode = NETIO_ERR_WRONG_PARAM; + uiPCount += 2; + do + hb_stackPop(); + while( --uiPCount ); + } + else + hb_vmProc( uiPCount ); + } + } + if( errCode == 0 && !fNoAnswer ) + { + if( uiMsg == NETIO_FUNC ) + { + ULONG itmSize; + char * itmData = hb_itemSerialize( hb_stackReturnItem(), TRUE, &itmSize ); + if( itmSize <= sizeof( buffer ) - NETIO_MSGLEN ) + msg = buffer; + else if( !ptr || itmSize > ( ULONG ) size - NETIO_MSGLEN ) + { + if( ptr ) + hb_xfree( ptr ); + ptr = msg = ( BYTE * ) hb_xgrab( itmSize + NETIO_MSGLEN ); + } + memcpy( msg + NETIO_MSGLEN, itmData, itmSize ); + hb_xfree( itmData ); + len = itmSize; + } + HB_PUT_LE_UINT32( &msg[ 0 ], uiMsg ); + HB_PUT_LE_UINT32( &msg[ 4 ], len ); + memset( msg + 8, '\0', NETIO_MSGLEN - 8 ); + } + } + } + break; + case NETIO_SYNC: continue; @@ -805,6 +925,7 @@ HB_FUNC( NETIO_SERVER ) if( fNoAnswer ) { + /* continue; */ /* do not send dummy record */ HB_PUT_LE_UINT32( &msg[ 0 ], NETIO_SYNC ); memset( msg + 4, '\0', NETIO_MSGLEN - 4 ); len = NETIO_MSGLEN; diff --git a/harbour/contrib/hbnetio/utils/netiosrv.prg b/harbour/contrib/hbnetio/utils/netiosrv.prg index 331f4de205..83c4e8562e 100644 --- a/harbour/contrib/hbnetio/utils/netiosrv.prg +++ b/harbour/contrib/hbnetio/utils/netiosrv.prg @@ -15,24 +15,41 @@ /* netio_mtserver() needs MT HVM version */ REQUEST HB_MT -PROCEDURE Main( port, ifaddr, root ) +/* enable this if you need all core functions in RPC support */ +//REQUEST __HB_EXTERN__ + +PROCEDURE Main( port, ifaddr, rootdir, rpc ) LOCAL pListenSocket HB_Logo() - pListenSocket := netio_mtserver( iif( port != NIL, Val( port ), ), ifaddr, root ) - IF Empty( pListenSocket ) - OutStd( "Cannot start server." + hb_osNewLine() ) + port := IIF( Empty( port ), 2941, Val( port ) ) + IF Empty( ifaddr ) + ifaddr := "0.0.0.0" + ENDIF + IF Empty( rootdir ) + rootdir := hb_dirBase() + ENDIF + rpc := !Empty( rpc ) + + IF port == 0 + HB_Usage() ELSE - OutStd( "Listening on: " + iif( ifaddr != NIL, ifaddr, "127.0.0.1" ) + ":" + iif( port != NIL, port, "2941" ) + hb_osNewLine() ) - OutStd( "Root filesystem: " + iif( root != NIL, root, hb_dirBase() ) + hb_osNewLine() ) + pListenSocket := netio_mtserver( port, ifaddr, rootdir, rpc ) + IF Empty( pListenSocket ) + OutStd( "Cannot start server." + hb_osNewLine() ) + ELSE + OutStd( "Listening on: " + ifaddr + ":" + hb_ntos( port ) + hb_osNewLine() ) + OutStd( "Root filesystem: " + rootdir + hb_osNewLine() ) + OutStd( "RPC support: " + iif( rpc, "enabled", "disabled" ) + hb_osNewLine() ) - OutStd( hb_osNewLine() ) - OutStd( "Press any key to stop NETIO server." + hb_osNewLine() ) - Inkey( 0 ) + OutStd( hb_osNewLine() ) + OutStd( "Press any key to stop NETIO server." + hb_osNewLine() ) + Inkey( 0 ) - netio_serverstop( pListenSocket ) - pListenSocket := NIL + netio_serverstop( pListenSocket ) + pListenSocket := NIL + ENDIF ENDIF RETURN @@ -48,7 +65,7 @@ STATIC PROCEDURE HB_Logo() STATIC PROCEDURE HB_Usage() - OutStd( "Syntax: netiosrv " + hb_osNewLine() ) + OutStd( "Syntax: netiosrv [] [] [] []" + hb_osNewLine() ) RETURN diff --git a/harbour/src/rtl/hbznet.c b/harbour/src/rtl/hbznet.c index 0ee07c7f3f..091341b1b7 100644 --- a/harbour/src/rtl/hbznet.c +++ b/harbour/src/rtl/hbznet.c @@ -60,7 +60,6 @@ #include "hbzlib.ch" #include -#include typedef struct _HB_ZNETSTREAM { @@ -74,6 +73,12 @@ HB_ZNETSTREAM; #define HB_ZNET_BUFSIZE 16384 +#if MAX_MEM_LEVEL >= 8 +# define HB_ZNET_MEM_LEVEL 8 +#else +# define HB_ZNET_MEM_LEVEL MAX_MEM_LEVEL +#endif + /* return status of last compression/decompression operation */ int hb_znetError( PHB_ZNETSTREAM pStream ) { @@ -105,7 +110,7 @@ PHB_ZNETSTREAM hb_znetOpen( int level, int strategy ) memset( pStream, 0, sizeof( HB_ZNETSTREAM ) ); if( deflateInit2( &pStream->wr, level, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy ) == Z_OK ) + Z_DEFLATED, -MAX_WBITS, HB_ZNET_MEM_LEVEL, strategy ) == Z_OK ) { pStream->wr.next_out = pStream->outbuf = ( Bytef * ) hb_xgrab( HB_ZNET_BUFSIZE ); pStream->wr.avail_out = HB_ZNET_BUFSIZE;