mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
f7b49ecd08
calls. This changes the generated RPC and IRPC calls to use the 'this' object pointer instead of requiring the passing of the object on each call. So typical usage is now: var echo = echo_init(); var io = irpcObj(); status = echo.connect("ncacn_np:server"); assert(status.is_ok); io.input.in_data = 7; status = echo.AddOne(io); assert(status.is_ok);
406 lines
9.7 KiB
C
406 lines
9.7 KiB
C
/*
|
|
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 02139, USA.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "scripting/ejs/smbcalls.h"
|
|
#include "lib/appweb/ejs/ejs.h"
|
|
#include "librpc/gen_ndr/ndr_echo.h"
|
|
#include "lib/cmdline/popt_common.h"
|
|
#include "lib/messaging/irpc.h"
|
|
#include "scripting/ejs/ejsrpc.h"
|
|
#include "dlinklist.h"
|
|
|
|
/*
|
|
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:
|
|
status = irpc.connect("smb_server");
|
|
*/
|
|
static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv)
|
|
{
|
|
NTSTATUS status;
|
|
int i;
|
|
struct event_context *ev;
|
|
struct ejs_irpc_connection *p;
|
|
struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
|
|
|
|
/* validate arguments */
|
|
if (argc != 1) {
|
|
ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
|
|
return -1;
|
|
}
|
|
|
|
p = talloc(this, struct ejs_irpc_connection);
|
|
if (p == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
p->server_name = argv[0];
|
|
|
|
ev = talloc_find_parent_bytype(mprMemCtx(), struct event_context);
|
|
|
|
/* 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 {
|
|
mprSetPtrChild(this, "irpc", p);
|
|
status = NT_STATUS_OK;
|
|
}
|
|
|
|
mpr_Return(eid, mprNTSTATUS(status));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
connect to an rpc server
|
|
examples:
|
|
status = rpc.connect("ncacn_ip_tcp:localhost");
|
|
status = rpc.connect("ncacn_ip_tcp:localhost", "pipe_name");
|
|
*/
|
|
static int ejs_rpc_connect(MprVarHandle eid, int argc, char **argv)
|
|
{
|
|
const char *binding, *pipe_name;
|
|
const struct dcerpc_interface_table *iface;
|
|
NTSTATUS status;
|
|
struct dcerpc_pipe *p;
|
|
struct cli_credentials *creds = cmdline_credentials;
|
|
struct event_context *ev;
|
|
struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
|
|
|
|
/* validate arguments */
|
|
if (argc < 1) {
|
|
ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
|
|
return -1;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
iface = idl_iface_by_name(pipe_name);
|
|
if (iface == NULL) {
|
|
status = NT_STATUS_OBJECT_NAME_INVALID;
|
|
goto done;
|
|
}
|
|
|
|
if (creds == NULL) {
|
|
creds = cli_credentials_init(mprMemCtx());
|
|
cli_credentials_guess(creds);
|
|
cli_credentials_set_username(creds, "", CRED_GUESSED);
|
|
cli_credentials_set_password(creds, "", CRED_GUESSED);
|
|
}
|
|
|
|
ev = talloc_find_parent_bytype(mprMemCtx(), struct event_context);
|
|
|
|
status = dcerpc_pipe_connect(this, &p, binding,
|
|
iface->uuid, iface->if_version,
|
|
creds, ev);
|
|
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;
|
|
|
|
/* by making the pipe a child of the connection variable, it will
|
|
auto close when it goes out of scope in the script */
|
|
mprSetPtrChild(this, "pipe", p);
|
|
mprSetPtr(this, "iface", iface);
|
|
|
|
done:
|
|
mpr_Return(eid, mprNTSTATUS(status));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
make an irpc call - called via the same interface as rpc
|
|
*/
|
|
static int ejs_irpc_call(int eid, struct MprVar *io,
|
|
const struct dcerpc_interface_table *iface, int callnum,
|
|
ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
|
|
{
|
|
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;
|
|
|
|
p = mprGetThisPtr(eid, "irpc");
|
|
|
|
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],
|
|
iface, callnum, ptr);
|
|
if (reqs[i] == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
talloc_steal(reqs, reqs[i]);
|
|
}
|
|
|
|
mprSetVar(io, "results", mprObject("results"));
|
|
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;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
backend code for making an rpc call - this is called from the pidl generated ejs
|
|
code
|
|
*/
|
|
int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
|
|
const struct dcerpc_interface_table *iface, int callnum,
|
|
ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
|
|
{
|
|
struct MprVar *io;
|
|
struct dcerpc_pipe *p;
|
|
NTSTATUS status;
|
|
void *ptr;
|
|
struct rpc_request *req;
|
|
struct ejs_rpc *ejs;
|
|
const struct dcerpc_interface_call *call;
|
|
|
|
if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
|
|
ejsSetErrorMsg(eid, "rpc_call invalid arguments");
|
|
return -1;
|
|
}
|
|
|
|
io = argv[0];
|
|
|
|
if (mprGetThisPtr(eid, "irpc")) {
|
|
/* its an irpc call */
|
|
return ejs_irpc_call(eid, io, iface, callnum, ejs_pull, ejs_push);
|
|
}
|
|
|
|
/* get the pipe info */
|
|
p = mprGetThisPtr(eid, "pipe");
|
|
if (p == NULL) {
|
|
ejsSetErrorMsg(eid, "rpc_call invalid pipe");
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* make the actual call */
|
|
req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
|
|
if (req == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
status = dcerpc_ndr_request_recv(req);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
status = ejs_push(ejs, io, ptr);
|
|
|
|
done:
|
|
talloc_free(ejs);
|
|
mpr_Return(eid, mprNTSTATUS(status));
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* a list of registered ejs rpc modules */
|
|
static struct ejs_register {
|
|
struct ejs_register *next, *prev;
|
|
const char *name;
|
|
MprCFunction fn;
|
|
} *ejs_registered;
|
|
|
|
/*
|
|
register a generated ejs module
|
|
*/
|
|
NTSTATUS smbcalls_register_ejs(const char *name, MprCFunction fn)
|
|
{
|
|
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;
|
|
r->fn = fn;
|
|
DLIST_ADD(ejs_registered, r);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/*
|
|
setup C functions that be called from ejs
|
|
*/
|
|
void smb_setup_ejs_rpc(void)
|
|
{
|
|
struct ejs_register *r;
|
|
|
|
for (r=ejs_registered;r;r=r->next) {
|
|
ejsDefineCFunction(-1, r->name, r->fn, NULL, MPR_VAR_SCRIPT_HANDLE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
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;
|
|
}
|