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