e666aa65ccf1743cfe3e034389e992708c4e4767
[garming/samba-autobuild/.git] / source4 / scripting / ejs / smbcalls_rpc.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    provide interfaces to rpc calls from ejs scripts
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "scripting/ejs/smbcalls.h"
25 #include "lib/appweb/ejs/ejs.h"
26 #include "librpc/gen_ndr/ndr_echo.h"
27 #include "lib/cmdline/popt_common.h"
28 #include "lib/messaging/irpc.h"
29 #include "scripting/ejs/ejsrpc.h"
30 #include "dlinklist.h"
31 #include "lib/events/events.h"
32 #include "librpc/rpc/dcerpc_table.h"
33 #include "auth/credentials/credentials.h"
34
35 /*
36   state of a irpc 'connection'
37 */
38 struct ejs_irpc_connection {
39         const char *server_name;
40         uint32_t *dest_ids;
41         struct messaging_context *msg_ctx;
42 };
43
44 /*
45   messaging clients need server IDs as well ...
46  */
47 #define EJS_ID_BASE 0x30000000
48
49 /*
50   setup a context for talking to a irpc server
51      example: 
52         status = irpc.connect("smb_server");
53 */
54 static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv)
55 {
56         NTSTATUS status;
57         int i;
58         struct event_context *ev;
59         struct ejs_irpc_connection *p;
60         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
61
62         /* validate arguments */
63         if (argc != 1) {
64                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
65                 return -1;
66         }
67
68         p = talloc(this, struct ejs_irpc_connection);
69         if (p == NULL) {
70                 return -1;
71         }
72
73         p->server_name = argv[0];
74
75         ev = event_context_find(p);
76
77         /* create a messaging context, looping as we have no way to
78            allocate temporary server ids automatically */
79         for (i=0;i<10000;i++) {
80                 p->msg_ctx = messaging_init(p, EJS_ID_BASE + i, ev);
81                 if (p->msg_ctx) break;
82         }
83         if (p->msg_ctx == NULL) {
84                 ejsSetErrorMsg(eid, "irpc_connect unable to create a messaging context");
85                 talloc_free(p);
86                 return -1;
87         }
88
89         p->dest_ids = irpc_servers_byname(p->msg_ctx, p->server_name);
90         if (p->dest_ids == NULL || p->dest_ids[0] == 0) {
91                 talloc_free(p);
92                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
93         } else {
94                 mprSetPtrChild(this, "irpc", p);
95                 status = NT_STATUS_OK;
96         }
97
98         mpr_Return(eid, mprNTSTATUS(status));
99         return 0;
100 }
101
102
103 /*
104   connect to an rpc server
105      examples: 
106         status = rpc.connect("ncacn_ip_tcp:localhost");
107         status = rpc.connect("ncacn_ip_tcp:localhost", "pipe_name");
108 */
109 static int ejs_rpc_connect(MprVarHandle eid, int argc, char **argv)
110 {
111         const char *binding, *pipe_name;
112         const struct dcerpc_interface_table *iface;
113         NTSTATUS status;
114         struct dcerpc_pipe *p;
115         struct cli_credentials *creds;
116         struct event_context *ev;
117         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
118         struct MprVar *credentials;
119
120         /* validate arguments */
121         if (argc < 1) {
122                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
123                 return -1;
124         }
125
126         binding    = argv[0];
127         if (strchr(binding, ':') == NULL) {
128                 /* its an irpc connect */
129                 return ejs_irpc_connect(eid, argc, argv);
130         }
131
132         if (argc > 1) {
133                 pipe_name = argv[1];
134         } else {
135                 pipe_name = mprToString(mprGetProperty(this, "pipe_name", NULL));
136         }
137
138         iface = idl_iface_by_name(pipe_name);
139         if (iface == NULL) {
140                 status = NT_STATUS_OBJECT_NAME_INVALID;
141                 goto done;
142         }
143
144         credentials = mprGetProperty(this, "credentials", NULL);
145         if (credentials) {
146                 creds = mprGetPtr(credentials, "creds");
147         } else {
148                 creds = cmdline_credentials;
149         }
150         if (creds == NULL) {
151                 creds = cli_credentials_init(mprMemCtx());
152                 cli_credentials_guess(creds);
153                 cli_credentials_set_anonymous(creds);
154         }
155
156         ev = event_context_find(mprMemCtx());
157
158         status = dcerpc_pipe_connect(this, &p, binding, iface, creds, ev);
159         if (!NT_STATUS_IS_OK(status)) goto done;
160
161         /* callers don't allocate ref vars in the ejs interface */
162         p->conn->flags |= DCERPC_NDR_REF_ALLOC;
163
164         /* by making the pipe a child of the connection variable, it will
165            auto close when it goes out of scope in the script */
166         mprSetPtrChild(this, "pipe", p);
167
168 done:
169         mpr_Return(eid, mprNTSTATUS(status));
170         return 0;
171 }
172
173
174 /*
175   make an irpc call - called via the same interface as rpc
176 */
177 static int ejs_irpc_call(int eid, struct MprVar *io, 
178                          const struct dcerpc_interface_table *iface, int callnum,
179                          ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
180 {
181         NTSTATUS status;
182         void *ptr;
183         struct ejs_rpc *ejs;
184         const struct dcerpc_interface_call *call;
185         struct ejs_irpc_connection *p;
186         struct irpc_request **reqs;
187         int i, count;
188         struct MprVar *results;
189
190         p = mprGetThisPtr(eid, "irpc");
191
192         ejs = talloc(mprMemCtx(), struct ejs_rpc);
193         if (ejs == NULL) {
194                 status = NT_STATUS_NO_MEMORY;
195                 goto done;
196         }
197
198         call = &iface->calls[callnum];
199
200         ejs->eid = eid;
201         ejs->callname = call->name;
202
203         /* allocate the C structure */
204         ptr = talloc_zero_size(ejs, call->struct_size);
205         if (ptr == NULL) {
206                 status = NT_STATUS_NO_MEMORY;
207                 goto done;
208         }
209
210         /* convert the mpr object into a C structure */
211         status = ejs_pull(ejs, io, ptr);
212         if (!NT_STATUS_IS_OK(status)) {
213                 goto done;
214         }
215
216         for (count=0;p->dest_ids[count];count++) /* noop */ ;
217
218         /* we need to make a call per server */
219         reqs = talloc_array(ejs, struct irpc_request *, count);
220         if (reqs == NULL) {
221                 status = NT_STATUS_NO_MEMORY;
222                 goto done;
223         }
224
225         /* make the actual calls */
226         for (i=0;i<count;i++) {
227                 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i], 
228                                          iface, callnum, ptr, ptr);
229                 if (reqs[i] == NULL) {
230                         status = NT_STATUS_NO_MEMORY;
231                         goto done;
232                 }
233                 talloc_steal(reqs, reqs[i]);
234         }
235         
236         mprSetVar(io, "results", mprObject("results"));
237         results = mprGetProperty(io, "results", NULL);
238
239         /* and receive the results, placing them in io.results[i] */
240         for (i=0;i<count;i++) {
241                 struct MprVar *output;
242
243                 status = irpc_call_recv(reqs[i]);
244                 if (!NT_STATUS_IS_OK(status)) {
245                         goto done;
246                 }
247                 status = ejs_push(ejs, io, ptr);
248                 if (!NT_STATUS_IS_OK(status)) {
249                         goto done;
250                 }
251
252                 /* add to the results array */
253                 output = mprGetProperty(io, "output", NULL);
254                 if (output) {
255                         char idx[16];
256                         mprItoa(i, idx, sizeof(idx));
257                         mprSetProperty(results, idx, output);
258                         mprDeleteProperty(io, "output");
259                 }
260         }
261         mprSetVar(results, "length", mprCreateIntegerVar(i));
262
263 done:
264         talloc_free(ejs);
265         mpr_Return(eid, mprNTSTATUS(status));
266         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
267                 return -1;
268         }
269         return 0;
270 }
271
272
273 /*
274   backend code for making an rpc call - this is called from the pidl generated ejs
275   code
276 */
277  int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
278                   const struct dcerpc_interface_table *iface, int callnum,
279                   ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
280 {
281         struct MprVar *io;
282         struct dcerpc_pipe *p;
283         NTSTATUS status;
284         void *ptr;
285         struct rpc_request *req;
286         struct ejs_rpc *ejs;
287         const struct dcerpc_interface_call *call;
288
289         if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
290                 ejsSetErrorMsg(eid, "rpc_call invalid arguments");
291                 return -1;
292         }
293             
294         io       = argv[0];
295
296         if (mprGetThisPtr(eid, "irpc")) {
297                 /* its an irpc call */
298                 return ejs_irpc_call(eid, io, iface, callnum, ejs_pull, ejs_push);
299         }
300
301         /* get the pipe info */
302         p = mprGetThisPtr(eid, "pipe");
303         if (p == NULL) {
304                 ejsSetErrorMsg(eid, "rpc_call invalid pipe");
305                 return -1;
306         }
307
308         ejs = talloc(mprMemCtx(), struct ejs_rpc);
309         if (ejs == NULL) {
310                 status = NT_STATUS_NO_MEMORY;
311                 goto done;
312         }
313
314         call = &iface->calls[callnum];
315
316         ejs->eid = eid;
317         ejs->callname = call->name;
318
319         /* allocate the C structure */
320         ptr = talloc_zero_size(ejs, call->struct_size);
321         if (ptr == NULL) {
322                 status = NT_STATUS_NO_MEMORY;
323                 goto done;
324         }
325
326         /* convert the mpr object into a C structure */
327         status = ejs_pull(ejs, io, ptr);
328         if (!NT_STATUS_IS_OK(status)) {
329                 goto done;
330         }
331
332         /* make the actual call */
333         req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
334
335         /* if requested, print the structure */
336         if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
337                 ndr_print_function_debug(call->ndr_print, call->name, NDR_IN, ptr);
338         }
339
340         if (req == NULL) {
341                 status = NT_STATUS_NO_MEMORY;
342                 goto done;
343         }
344
345         status = dcerpc_ndr_request_recv(req);
346         if (!NT_STATUS_IS_OK(status)) {
347                 goto done;
348         }
349
350         /* print the 'out' structure, if needed */
351         if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
352                 ndr_print_function_debug(call->ndr_print, call->name, NDR_OUT, ptr);
353         }
354
355         status = ejs_push(ejs, io, ptr);
356
357 done:
358         talloc_free(ejs);
359         mpr_Return(eid, mprNTSTATUS(status));
360         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
361                 return -1;
362         }
363         return 0;
364 }
365
366 /*
367   hook called by generated RPC interfaces at the end of their init routines
368   used to add generic operations on the pipe
369 */
370 int ejs_rpc_init(struct MprVar *obj, const char *name)
371 {
372         dcerpc_table_init();
373
374         mprSetStringCFunction(obj, "connect", ejs_rpc_connect);
375         if (mprGetProperty(obj, "pipe_name", NULL) == NULL) {
376                 mprSetVar(obj, "pipe_name", mprString(name));
377         }
378         return 0;
379 }