/* 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 3 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, see . */ #include "includes.h" #include "scripting/ejs/smbcalls.h" #include "lib/appweb/ejs/ejs.h" #include "librpc/gen_ndr/echo.h" #include "lib/cmdline/popt_common.h" #include "lib/messaging/irpc.h" #include "scripting/ejs/ejsrpc.h" #include "lib/util/dlinklist.h" #include "lib/events/events.h" #include "librpc/ndr/ndr_table.h" #include "auth/credentials/credentials.h" #include "librpc/rpc/dcerpc.h" #include "cluster/cluster.h" /* state of a irpc 'connection' */ struct ejs_irpc_connection { const char *server_name; struct server_id *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 = mprEventCtx(); /* 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, lp_messaging_path(p, mprLpCtx()), cluster_id(EJS_ID_BASE, i), lp_iconv_convenience(mprLpCtx()), 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, p->server_name); if (p->dest_ids == NULL || p->dest_ids[0].id == 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 ndr_interface_table *iface; NTSTATUS status; struct dcerpc_pipe *p; struct cli_credentials *creds; struct event_context *ev; struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0); struct MprVar *credentials; /* 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 = ndr_table_by_name(pipe_name); if (iface == NULL) { status = NT_STATUS_OBJECT_NAME_INVALID; goto done; } credentials = mprGetProperty(this, "credentials", NULL); if (credentials) { creds = (struct cli_credentials *) mprGetPtr(credentials, "creds"); } else { creds = cmdline_credentials; } if (creds == NULL) { creds = cli_credentials_init(mprMemCtx()); cli_credentials_set_anonymous(creds); } ev = mprEventCtx(); status = dcerpc_pipe_connect(this, &p, binding, iface, creds, ev, mprLpCtx()); 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); 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 ndr_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 ndr_interface_call *call; struct ejs_irpc_connection *p; struct irpc_request **reqs; int i, count; struct MprVar *results; p = (struct ejs_irpc_connection *)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].id;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;imsg_ctx, p->dest_ids[i], iface, callnum, ptr, 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;itype != 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; } /* make the actual call */ req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr); /* 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; } status = dcerpc_ndr_request_recv(req); if (!NT_STATUS_IS_OK(status)) { goto done; } /* 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; } /* 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) { ndr_table_init(); mprSetStringCFunction(obj, "connect", ejs_rpc_connect); if (mprGetProperty(obj, "pipe_name", NULL) == NULL) { mprSetVar(obj, "pipe_name", mprString(name)); } return 0; }