6aef216ca48dc5f447dee66c1c2239a760b515a0
[jelmer/samba4-debian.git] / source / 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/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
32 /*
33   state of a irpc 'connection'
34 */
35 struct ejs_irpc_connection {
36         const char *server_name;
37         uint32_t *dest_ids;
38         struct messaging_context *msg_ctx;
39 };
40
41 /*
42   messaging clients need server IDs as well ...
43  */
44 #define EJS_ID_BASE 0x30000000
45
46 /*
47   setup a context for talking to a irpc server
48      example: 
49         var conn = new Object();
50         status = irpc_connect(conn, "smb_server");
51 */
52 static int ejs_irpc_connect(MprVarHandle eid, int argc, struct MprVar **argv)
53 {
54         NTSTATUS status;
55         int i;
56         struct MprVar *conn;
57         struct event_context *ev;
58         struct ejs_irpc_connection *p;
59
60         /* validate arguments */
61         if (argc != 2 ||
62             argv[0]->type != MPR_TYPE_OBJECT ||
63             argv[1]->type != MPR_TYPE_STRING) {
64                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
65                 return -1;
66         }
67
68         conn           = argv[0];
69
70         p = talloc(conn, struct ejs_irpc_connection);
71         if (p == NULL) {
72                 return -1;
73         }
74
75         p->server_name = mprToString(argv[1]);
76
77         ev = talloc_find_parent_bytype(mprMemCtx(), struct event_context);
78
79         /* create a messaging context, looping as we have no way to
80            allocate temporary server ids automatically */
81         for (i=0;i<10000;i++) {
82                 p->msg_ctx = messaging_init(p, EJS_ID_BASE + i, ev);
83                 if (p->msg_ctx) break;
84         }
85         if (p->msg_ctx == NULL) {
86                 ejsSetErrorMsg(eid, "irpc_connect unable to create a messaging context");
87                 talloc_free(p);
88                 return -1;
89         }
90
91         p->dest_ids = irpc_servers_byname(p->msg_ctx, p->server_name);
92         if (p->dest_ids == NULL || p->dest_ids[0] == 0) {
93                 talloc_free(p);
94                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
95         } else {
96                 mprSetPtrChild(conn, "irpc", p);
97                 status = NT_STATUS_OK;
98         }
99
100         mpr_Return(eid, mprNTSTATUS(status));
101         return 0;
102 }
103
104
105 /*
106   connect to an rpc server
107      example: 
108         var conn = new Object();
109         status = rpc_connect(conn, "ncacn_ip_tcp:localhost", "rpcecho");
110 */
111 static int ejs_rpc_connect(MprVarHandle eid, int argc, struct MprVar **argv)
112 {
113         const char *binding, *pipe_name;
114         const struct dcerpc_interface_table *iface;
115         NTSTATUS status;
116         struct dcerpc_pipe *p;
117         struct MprVar *conn;
118         struct cli_credentials *creds = cmdline_credentials;
119         struct event_context *ev;
120
121         /* validate arguments */
122         if (argc != 3 ||
123             argv[0]->type != MPR_TYPE_OBJECT ||
124             argv[1]->type != MPR_TYPE_STRING ||
125             argv[2]->type != MPR_TYPE_STRING) {
126                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
127                 return -1;
128         }
129
130         conn       = argv[0];
131         binding    = mprToString(argv[1]);
132         pipe_name  = mprToString(argv[2]);
133
134         iface = idl_iface_by_name(pipe_name);
135         if (iface == NULL) {
136                 status = NT_STATUS_OBJECT_NAME_INVALID;
137                 goto done;
138         }
139
140         if (creds == NULL) {
141                 creds = cli_credentials_init(mprMemCtx());
142                 cli_credentials_guess(creds);
143                 cli_credentials_set_username(creds, "", CRED_GUESSED);
144                 cli_credentials_set_password(creds, "", CRED_GUESSED);
145         }
146
147         ev = talloc_find_parent_bytype(mprMemCtx(), struct event_context);
148
149         status = dcerpc_pipe_connect(conn, &p, binding, 
150                                      iface->uuid, iface->if_version, 
151                                      creds, ev);
152         if (!NT_STATUS_IS_OK(status)) goto done;
153
154         /* callers don't allocate ref vars in the ejs interface */
155         p->conn->flags |= DCERPC_NDR_REF_ALLOC;
156
157         /* by making the pipe a child of the connection variable, it will
158            auto close when it goes out of scope in the script */
159         mprSetPtrChild(conn, "pipe", p);
160         mprSetPtr(conn, "iface", iface);
161
162 done:
163         mpr_Return(eid, mprNTSTATUS(status));
164         return 0;
165 }
166
167
168 /*
169   make an irpc call - called via the same interface as rpc
170 */
171 static int ejs_irpc_call(int eid, struct MprVar *conn, struct MprVar *io, 
172                          const struct dcerpc_interface_table *iface, int callnum,
173                          ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
174 {
175         NTSTATUS status;
176         void *ptr;
177         struct ejs_rpc *ejs;
178         const struct dcerpc_interface_call *call;
179         struct ejs_irpc_connection *p;
180         struct irpc_request **reqs;
181         int i, count;
182         struct MprVar *results;
183
184         p = mprGetPtr(conn, "irpc");
185
186         ejs = talloc(mprMemCtx(), struct ejs_rpc);
187         if (ejs == NULL) {
188                 status = NT_STATUS_NO_MEMORY;
189                 goto done;
190         }
191
192         call = &iface->calls[callnum];
193
194         ejs->eid = eid;
195         ejs->callname = call->name;
196
197         /* allocate the C structure */
198         ptr = talloc_zero_size(ejs, call->struct_size);
199         if (ptr == NULL) {
200                 status = NT_STATUS_NO_MEMORY;
201                 goto done;
202         }
203
204         /* convert the mpr object into a C structure */
205         status = ejs_pull(ejs, io, ptr);
206         if (!NT_STATUS_IS_OK(status)) {
207                 goto done;
208         }
209
210         for (count=0;p->dest_ids[count];count++) /* noop */ ;
211
212         /* we need to make a call per server */
213         reqs = talloc_array(ejs, struct irpc_request *, count);
214         if (reqs == NULL) {
215                 status = NT_STATUS_NO_MEMORY;
216                 goto done;
217         }
218
219         /* make the actual calls */
220         for (i=0;i<count;i++) {
221                 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i], 
222                                          iface, callnum, ptr);
223                 if (reqs[i] == NULL) {
224                         status = NT_STATUS_NO_MEMORY;
225                         goto done;
226                 }
227                 talloc_steal(reqs, reqs[i]);
228         }
229         
230         mprSetVar(io, "results", mprObject("results"));
231         results = mprGetProperty(io, "results", NULL);
232
233         /* and receive the results, placing them in io.results[i] */
234         for (i=0;i<count;i++) {
235                 struct MprVar *output;
236
237                 status = irpc_call_recv(reqs[i]);
238                 if (!NT_STATUS_IS_OK(status)) {
239                         goto done;
240                 }
241                 status = ejs_push(ejs, io, ptr);
242                 if (!NT_STATUS_IS_OK(status)) {
243                         goto done;
244                 }
245                 talloc_free(reqs[i]);
246
247                 /* add to the results array */
248                 output = mprGetProperty(io, "output", NULL);
249                 if (output) {
250                         char idx[16];
251                         mprItoa(i, idx, sizeof(idx));
252                         mprSetProperty(results, idx, output);
253                         mprDeleteProperty(io, "output");
254                 }
255         }
256         mprSetVar(results, "length", mprCreateIntegerVar(i));
257
258 done:
259         talloc_free(ejs);
260         mpr_Return(eid, mprNTSTATUS(status));
261         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
262                 return -1;
263         }
264         return 0;
265 }
266
267
268 /*
269   backend code for making an rpc call - this is called from the pidl generated ejs
270   code
271 */
272  int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
273                   const struct dcerpc_interface_table *iface, int callnum,
274                   ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
275 {
276         struct MprVar *conn, *io;
277         struct dcerpc_pipe *p;
278         NTSTATUS status;
279         void *ptr;
280         struct rpc_request *req;
281         struct ejs_rpc *ejs;
282         const struct dcerpc_interface_call *call;
283
284         if (argc != 2 ||
285             argv[0]->type != MPR_TYPE_OBJECT ||
286             argv[1]->type != MPR_TYPE_OBJECT) {
287                 ejsSetErrorMsg(eid, "rpc_call invalid arguments");
288                 return -1;
289         }
290             
291         conn     = argv[0];
292         io       = argv[1];
293
294         if (mprGetPtr(conn, "irpc")) {
295                 /* its an irpc call */
296                 return ejs_irpc_call(eid, conn, io, iface, callnum, ejs_pull, ejs_push);
297         }
298
299         /* get the pipe info */
300         p = mprGetPtr(conn, "pipe");
301         if (p == NULL) {
302                 ejsSetErrorMsg(eid, "rpc_call invalid pipe");
303                 return -1;
304         }
305
306         ejs = talloc(mprMemCtx(), struct ejs_rpc);
307         if (ejs == NULL) {
308                 status = NT_STATUS_NO_MEMORY;
309                 goto done;
310         }
311
312         call = &iface->calls[callnum];
313
314         ejs->eid = eid;
315         ejs->callname = call->name;
316
317         /* allocate the C structure */
318         ptr = talloc_zero_size(ejs, call->struct_size);
319         if (ptr == NULL) {
320                 status = NT_STATUS_NO_MEMORY;
321                 goto done;
322         }
323
324         /* convert the mpr object into a C structure */
325         status = ejs_pull(ejs, io, ptr);
326         if (!NT_STATUS_IS_OK(status)) {
327                 goto done;
328         }
329
330         /* if requested, print the structure */
331         if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
332                 ndr_print_function_debug(call->ndr_print, call->name, NDR_IN, ptr);
333         }
334
335         /* make the actual call */
336         req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
337         if (req == NULL) {
338                 status = NT_STATUS_NO_MEMORY;
339                 goto done;
340         }
341         status = dcerpc_ndr_request_recv(req);
342
343         /* print the 'out' structure, if needed */
344         if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
345                 ndr_print_function_debug(call->ndr_print, call->name, NDR_OUT, ptr);
346         }
347
348         status = ejs_push(ejs, io, ptr);
349
350 done:
351         talloc_free(ejs);
352         mpr_Return(eid, mprNTSTATUS(status));
353         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
354                 return -1;
355         }
356         return 0;
357 }
358
359
360 /* a list of registered ejs rpc modules */
361 static struct ejs_register {
362         struct ejs_register *next, *prev;
363         const char *name;
364         ejs_setup_t setup;
365         ejs_constants_t constants;
366 } *ejs_registered;
367
368 /*
369   register a generated ejs module
370 */
371  NTSTATUS smbcalls_register_ejs(const char *name, 
372                                 ejs_setup_t setup,
373                                 ejs_constants_t constants)
374 {
375         struct ejs_register *r;
376         void *ctx = ejs_registered;
377         if (ctx == NULL) {
378                 ctx = talloc_autofree_context();
379         }
380         r = talloc(ctx, struct ejs_register);
381         NT_STATUS_HAVE_NO_MEMORY(r);
382         r->name = name;
383         r->setup = setup;
384         r->constants = constants;
385         DLIST_ADD(ejs_registered, r);
386         return NT_STATUS_OK;
387 }
388
389 /*
390   setup C functions that be called from ejs
391 */
392 void smb_setup_ejs_rpc(void)
393 {
394         struct ejs_register *r;
395
396         ejsDefineCFunction(-1, "rpc_connect", ejs_rpc_connect, NULL, MPR_VAR_SCRIPT_HANDLE);
397         ejsDefineCFunction(-1, "irpc_connect", ejs_irpc_connect, NULL, MPR_VAR_SCRIPT_HANDLE);
398         for (r=ejs_registered;r;r=r->next) {
399                 r->setup();
400         }
401 }
402
403 /*
404   setup constants for rpc calls
405 */
406 void smb_setup_ejs_rpc_constants(int eid)
407 {
408         struct ejs_register *r;
409         struct MprVar v;
410
411         for (r=ejs_registered;r;r=r->next) {
412                 r->constants(eid);
413         }
414
415         v = mprCreatePtrVar(NULL);
416         mprSetProperty(ejsGetGlobalObject(eid), "NULL", &v);
417 }
418
419