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