2005-07-02 09:21:17 +04:00
/*
Unix SMB / CIFS implementation .
provide interfaces to rpc calls from ejs scripts
Copyright ( C ) Andrew Tridgell 2005
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 of the License , 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 program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2005-07-10 06:37:50 +04:00
# include "scripting/ejs/smbcalls.h"
2005-07-13 04:06:38 +04:00
# include "lib/appweb/ejs/ejs.h"
2005-07-02 09:21:17 +04:00
# include "librpc/gen_ndr/ndr_echo.h"
# include "lib/cmdline/popt_common.h"
2005-07-10 10:21:03 +04:00
# include "lib/messaging/irpc.h"
2005-07-02 15:12:33 +04:00
# include "scripting/ejs/ejsrpc.h"
2005-07-09 15:48:59 +04:00
# include "dlinklist.h"
2005-08-28 06:37:14 +04:00
# include "lib/events/events.h"
2005-07-02 09:21:17 +04:00
2005-07-10 10:21:03 +04:00
/*
state of a irpc ' connection '
*/
struct ejs_irpc_connection {
const char * server_name ;
uint32_t * dest_ids ;
struct messaging_context * msg_ctx ;
} ;
/*
messaging clients need server IDs as well . . .
*/
# define EJS_ID_BASE 0x30000000
/*
setup a context for talking to a irpc server
example :
2005-07-28 10:46:03 +04:00
status = irpc . connect ( " smb_server " ) ;
2005-07-10 10:21:03 +04:00
*/
2005-07-28 10:46:03 +04:00
static int ejs_irpc_connect ( MprVarHandle eid , int argc , char * * argv )
2005-07-10 10:21:03 +04:00
{
NTSTATUS status ;
int i ;
struct event_context * ev ;
struct ejs_irpc_connection * p ;
2005-07-28 10:46:03 +04:00
struct MprVar * this = mprGetProperty ( ejsGetLocalObject ( eid ) , " this " , 0 ) ;
2005-07-10 10:21:03 +04:00
/* validate arguments */
2005-07-28 10:46:03 +04:00
if ( argc ! = 1 ) {
2005-07-10 10:21:03 +04:00
ejsSetErrorMsg ( eid , " rpc_connect invalid arguments " ) ;
return - 1 ;
}
2005-07-28 10:46:03 +04:00
p = talloc ( this , struct ejs_irpc_connection ) ;
2005-07-10 10:21:03 +04:00
if ( p = = NULL ) {
return - 1 ;
}
2005-07-28 10:46:03 +04:00
p - > server_name = argv [ 0 ] ;
2005-07-10 10:21:03 +04:00
2005-08-28 06:37:14 +04:00
ev = event_context_find ( p ) ;
2005-07-10 10:21:03 +04:00
/* create a messaging context, looping as we have no way to
allocate temporary server ids automatically */
for ( i = 0 ; i < 10000 ; i + + ) {
p - > msg_ctx = messaging_init ( p , EJS_ID_BASE + i , ev ) ;
if ( p - > msg_ctx ) break ;
}
if ( p - > msg_ctx = = NULL ) {
ejsSetErrorMsg ( eid , " irpc_connect unable to create a messaging context " ) ;
talloc_free ( p ) ;
return - 1 ;
}
p - > dest_ids = irpc_servers_byname ( p - > msg_ctx , p - > server_name ) ;
if ( p - > dest_ids = = NULL | | p - > dest_ids [ 0 ] = = 0 ) {
talloc_free ( p ) ;
status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
} else {
2005-07-28 10:46:03 +04:00
mprSetPtrChild ( this , " irpc " , p ) ;
2005-07-10 12:35:18 +04:00
status = NT_STATUS_OK ;
2005-07-10 10:21:03 +04:00
}
mpr_Return ( eid , mprNTSTATUS ( status ) ) ;
return 0 ;
}
2005-07-02 09:21:17 +04:00
/*
connect to an rpc server
2005-07-28 10:46:03 +04:00
examples :
status = rpc . connect ( " ncacn_ip_tcp:localhost " ) ;
status = rpc . connect ( " ncacn_ip_tcp:localhost " , " pipe_name " ) ;
2005-07-02 09:21:17 +04:00
*/
2005-07-28 10:46:03 +04:00
static int ejs_rpc_connect ( MprVarHandle eid , int argc , char * * argv )
2005-07-02 09:21:17 +04:00
{
const char * binding , * pipe_name ;
const struct dcerpc_interface_table * iface ;
NTSTATUS status ;
struct dcerpc_pipe * p ;
2005-08-07 10:13:55 +04:00
struct cli_credentials * creds ;
2005-07-09 09:28:42 +04:00
struct event_context * ev ;
2005-07-28 10:46:03 +04:00
struct MprVar * this = mprGetProperty ( ejsGetLocalObject ( eid ) , " this " , 0 ) ;
2005-08-08 07:20:17 +04:00
struct MprVar * credentials ;
2005-07-02 09:21:17 +04:00
/* validate arguments */
2005-07-28 10:46:03 +04:00
if ( argc < 1 ) {
2005-07-02 09:21:17 +04:00
ejsSetErrorMsg ( eid , " rpc_connect invalid arguments " ) ;
return - 1 ;
}
2005-07-28 10:46:03 +04:00
binding = argv [ 0 ] ;
if ( strchr ( binding , ' : ' ) = = NULL ) {
/* its an irpc connect */
return ejs_irpc_connect ( eid , argc , argv ) ;
}
if ( argc > 1 ) {
pipe_name = argv [ 1 ] ;
} else {
pipe_name = mprToString ( mprGetProperty ( this , " pipe_name " , NULL ) ) ;
}
2005-07-02 09:21:17 +04:00
iface = idl_iface_by_name ( pipe_name ) ;
if ( iface = = NULL ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto done ;
}
2005-08-08 07:20:17 +04:00
credentials = mprGetProperty ( this , " credentials " , NULL ) ;
if ( credentials ) {
creds = mprGetPtr ( credentials , " creds " ) ;
} else {
2005-08-07 10:13:55 +04:00
creds = cmdline_credentials ;
}
2005-07-09 09:28:42 +04:00
if ( creds = = NULL ) {
creds = cli_credentials_init ( mprMemCtx ( ) ) ;
cli_credentials_guess ( creds ) ;
2005-08-07 10:13:55 +04:00
cli_credentials_set_anonymous ( creds ) ;
2005-07-09 09:28:42 +04:00
}
2005-08-28 06:37:14 +04:00
ev = event_context_find ( mprMemCtx ( ) ) ;
2005-07-09 09:28:42 +04:00
2005-07-28 10:46:03 +04:00
status = dcerpc_pipe_connect ( this , & p , binding ,
2005-07-02 09:21:17 +04:00
iface - > uuid , iface - > if_version ,
2005-07-09 09:28:42 +04:00
creds , ev ) ;
2005-07-02 15:12:33 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto done ;
/* callers don't allocate ref vars in the ejs interface */
p - > conn - > flags | = DCERPC_NDR_REF_ALLOC ;
2005-07-10 10:21:03 +04:00
/* by making the pipe a child of the connection variable, it will
auto close when it goes out of scope in the script */
2005-07-28 10:46:03 +04:00
mprSetPtrChild ( this , " pipe " , p ) ;
2005-07-02 09:21:17 +04:00
done :
2005-07-10 06:37:50 +04:00
mpr_Return ( eid , mprNTSTATUS ( status ) ) ;
2005-07-02 09:21:17 +04:00
return 0 ;
}
/*
2005-07-10 10:51:00 +04:00
make an irpc call - called via the same interface as rpc
*/
2005-07-28 10:46:03 +04:00
static int ejs_irpc_call ( int eid , struct MprVar * io ,
2005-07-10 10:51:00 +04:00
const struct dcerpc_interface_table * iface , int callnum ,
ejs_pull_function_t ejs_pull , ejs_push_function_t ejs_push )
{
2005-07-10 12:35:18 +04:00
NTSTATUS status ;
void * ptr ;
struct ejs_rpc * ejs ;
const struct dcerpc_interface_call * call ;
struct ejs_irpc_connection * p ;
struct irpc_request * * reqs ;
int i , count ;
struct MprVar * results ;
2005-07-28 10:46:03 +04:00
p = mprGetThisPtr ( eid , " irpc " ) ;
2005-07-10 12:35:18 +04:00
ejs = talloc ( mprMemCtx ( ) , struct ejs_rpc ) ;
if ( ejs = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
call = & iface - > calls [ callnum ] ;
ejs - > eid = eid ;
ejs - > callname = call - > name ;
/* allocate the C structure */
ptr = talloc_zero_size ( ejs , call - > struct_size ) ;
if ( ptr = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
/* convert the mpr object into a C structure */
status = ejs_pull ( ejs , io , ptr ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
for ( count = 0 ; p - > dest_ids [ count ] ; count + + ) /* noop */ ;
/* we need to make a call per server */
reqs = talloc_array ( ejs , struct irpc_request * , count ) ;
if ( reqs = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
/* make the actual calls */
for ( i = 0 ; i < count ; i + + ) {
reqs [ i ] = irpc_call_send ( p - > msg_ctx , p - > dest_ids [ i ] ,
2005-08-01 21:33:43 +04:00
iface , callnum , ptr , ptr ) ;
2005-07-10 12:35:18 +04:00
if ( reqs [ i ] = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
talloc_steal ( reqs , reqs [ i ] ) ;
}
2005-07-11 14:18:26 +04:00
mprSetVar ( io , " results " , mprObject ( " results " ) ) ;
2005-07-10 12:35:18 +04:00
results = mprGetProperty ( io , " results " , NULL ) ;
/* and receive the results, placing them in io.results[i] */
for ( i = 0 ; i < count ; i + + ) {
struct MprVar * output ;
status = irpc_call_recv ( reqs [ i ] ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
status = ejs_push ( ejs , io , ptr ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
talloc_free ( reqs [ i ] ) ;
/* add to the results array */
output = mprGetProperty ( io , " output " , NULL ) ;
if ( output ) {
char idx [ 16 ] ;
mprItoa ( i , idx , sizeof ( idx ) ) ;
mprSetProperty ( results , idx , output ) ;
mprDeleteProperty ( io , " output " ) ;
}
}
mprSetVar ( results , " length " , mprCreateIntegerVar ( i ) ) ;
done :
talloc_free ( ejs ) ;
mpr_Return ( eid , mprNTSTATUS ( status ) ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INTERNAL_ERROR ) ) {
return - 1 ;
}
2005-07-10 10:51:00 +04:00
return 0 ;
}
/*
backend code for making an rpc call - this is called from the pidl generated ejs
code
2005-07-02 09:21:17 +04:00
*/
2005-07-07 10:25:04 +04:00
int ejs_rpc_call ( int eid , int argc , struct MprVar * * argv ,
2005-07-10 10:51:00 +04:00
const struct dcerpc_interface_table * iface , int callnum ,
2005-07-07 10:25:04 +04:00
ejs_pull_function_t ejs_pull , ejs_push_function_t ejs_push )
2005-07-02 09:21:17 +04:00
{
2005-07-28 10:46:03 +04:00
struct MprVar * io ;
2005-07-02 15:12:33 +04:00
struct dcerpc_pipe * p ;
2005-07-02 09:21:17 +04:00
NTSTATUS status ;
2005-07-02 15:12:33 +04:00
void * ptr ;
struct rpc_request * req ;
2005-07-10 05:10:09 +04:00
struct ejs_rpc * ejs ;
2005-07-10 10:51:00 +04:00
const struct dcerpc_interface_call * call ;
2005-07-02 09:21:17 +04:00
2005-07-28 10:46:03 +04:00
if ( argc ! = 1 | | argv [ 0 ] - > type ! = MPR_TYPE_OBJECT ) {
2005-07-02 09:21:17 +04:00
ejsSetErrorMsg ( eid , " rpc_call invalid arguments " ) ;
return - 1 ;
}
2005-07-28 10:46:03 +04:00
io = argv [ 0 ] ;
2005-07-02 09:21:17 +04:00
2005-07-28 10:46:03 +04:00
if ( mprGetThisPtr ( eid , " irpc " ) ) {
2005-07-10 10:51:00 +04:00
/* its an irpc call */
2005-07-28 10:46:03 +04:00
return ejs_irpc_call ( eid , io , iface , callnum , ejs_pull , ejs_push ) ;
2005-07-10 10:51:00 +04:00
}
2005-07-02 15:12:33 +04:00
/* get the pipe info */
2005-07-28 10:46:03 +04:00
p = mprGetThisPtr ( eid , " pipe " ) ;
2005-07-10 10:51:00 +04:00
if ( p = = NULL ) {
2005-07-02 09:21:17 +04:00
ejsSetErrorMsg ( eid , " rpc_call invalid pipe " ) ;
return - 1 ;
}
2005-07-10 05:10:09 +04:00
ejs = talloc ( mprMemCtx ( ) , struct ejs_rpc ) ;
if ( ejs = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
2005-07-10 10:51:00 +04:00
call = & iface - > calls [ callnum ] ;
2005-07-10 05:10:09 +04:00
ejs - > eid = eid ;
2005-07-10 10:51:00 +04:00
ejs - > callname = call - > name ;
2005-07-10 05:10:09 +04:00
2005-07-02 15:12:33 +04:00
/* allocate the C structure */
2005-07-10 05:10:09 +04:00
ptr = talloc_zero_size ( ejs , call - > struct_size ) ;
2005-07-02 15:12:33 +04:00
if ( ptr = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
/* convert the mpr object into a C structure */
2005-07-10 05:10:09 +04:00
status = ejs_pull ( ejs , io , ptr ) ;
2005-07-02 15:12:33 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
2005-08-17 05:30:47 +04:00
/* make the actual call */
req = dcerpc_ndr_request_send ( p , NULL , iface , callnum , ptr , ptr ) ;
2005-07-02 15:12:33 +04:00
/* if requested, print the structure */
if ( p - > conn - > flags & DCERPC_DEBUG_PRINT_IN ) {
ndr_print_function_debug ( call - > ndr_print , call - > name , NDR_IN , ptr ) ;
}
if ( req = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
2005-08-17 05:30:47 +04:00
2005-07-02 15:12:33 +04:00
status = dcerpc_ndr_request_recv ( req ) ;
2005-08-10 10:55:46 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
2005-07-02 15:12:33 +04:00
/* print the 'out' structure, if needed */
if ( p - > conn - > flags & DCERPC_DEBUG_PRINT_OUT ) {
ndr_print_function_debug ( call - > ndr_print , call - > name , NDR_OUT , ptr ) ;
}
2005-07-10 05:10:09 +04:00
status = ejs_push ( ejs , io , ptr ) ;
2005-07-02 15:12:33 +04:00
done :
2005-07-10 05:10:09 +04:00
talloc_free ( ejs ) ;
2005-07-10 06:37:50 +04:00
mpr_Return ( eid , mprNTSTATUS ( status ) ) ;
2005-07-07 12:32:35 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INTERNAL_ERROR ) ) {
return - 1 ;
}
2005-07-02 09:21:17 +04:00
return 0 ;
}
2005-07-09 15:48:59 +04:00
/* a list of registered ejs rpc modules */
static struct ejs_register {
struct ejs_register * next , * prev ;
const char * name ;
2005-07-15 11:18:23 +04:00
MprCFunction fn ;
2005-07-09 15:48:59 +04:00
} * ejs_registered ;
/*
register a generated ejs module
*/
2005-07-15 11:18:23 +04:00
NTSTATUS smbcalls_register_ejs ( const char * name , MprCFunction fn )
2005-07-09 15:48:59 +04:00
{
struct ejs_register * r ;
void * ctx = ejs_registered ;
if ( ctx = = NULL ) {
ctx = talloc_autofree_context ( ) ;
}
r = talloc ( ctx , struct ejs_register ) ;
NT_STATUS_HAVE_NO_MEMORY ( r ) ;
r - > name = name ;
2005-07-15 11:18:23 +04:00
r - > fn = fn ;
2005-07-09 15:48:59 +04:00
DLIST_ADD ( ejs_registered , r ) ;
return NT_STATUS_OK ;
}
2005-07-02 09:21:17 +04:00
/*
setup C functions that be called from ejs
*/
void smb_setup_ejs_rpc ( void )
{
2005-07-09 15:48:59 +04:00
struct ejs_register * r ;
2005-07-08 14:29:18 +04:00
2005-07-09 15:48:59 +04:00
for ( r = ejs_registered ; r ; r = r - > next ) {
2005-07-15 11:18:23 +04:00
ejsDefineCFunction ( - 1 , r - > name , r - > fn , NULL , MPR_VAR_SCRIPT_HANDLE ) ;
2005-07-09 15:48:59 +04:00
}
2005-07-02 09:21:17 +04:00
}
2005-07-28 10:46:03 +04:00
/*
hook called by generated RPC interfaces at the end of their init routines
used to add generic operations on the pipe
*/
int ejs_rpc_init ( struct MprVar * obj , const char * name )
{
mprSetStringCFunction ( obj , " connect " , ejs_rpc_connect ) ;
if ( mprGetProperty ( obj , " pipe_name " , NULL ) = = NULL ) {
mprSetVar ( obj , " pipe_name " , mprString ( name ) ) ;
}
return 0 ;
}