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