Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
4 :
5 : Based on the equivalent for EJS:
6 : Copyright © Andrew Tridgell <tridge@samba.org> 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 "lib/replace/system/python.h"
23 : #include "python/py3compat.h"
24 : #include "includes.h"
25 : #include "python/modules.h"
26 : #include "libcli/util/pyerrors.h"
27 : #include "librpc/rpc/pyrpc_util.h"
28 : #include "librpc/ndr/libndr.h"
29 : #include "lib/messaging/messaging.h"
30 : #include "lib/messaging/irpc.h"
31 : #include "lib/events/events.h"
32 : #include "cluster/cluster.h"
33 : #include "param/param.h"
34 : #include "param/pyparam.h"
35 : #include "librpc/rpc/dcerpc.h"
36 : #include "librpc/gen_ndr/server_id.h"
37 : #include <pytalloc.h>
38 : #include "messaging_internal.h"
39 :
40 :
41 : extern PyTypeObject imessaging_Type;
42 :
43 52 : static bool server_id_from_py(PyObject *object, struct server_id *server_id)
44 : {
45 0 : Py_ssize_t tuple_size;
46 :
47 52 : if (!PyTuple_Check(object)) {
48 4 : if (!py_check_dcerpc_type(object, "samba.dcerpc.server_id", "server_id")) {
49 :
50 0 : PyErr_SetString(PyExc_ValueError, "Expected tuple or server_id");
51 0 : return false;
52 : }
53 4 : *server_id = *pytalloc_get_type(object, struct server_id);
54 4 : return true;
55 : }
56 :
57 48 : tuple_size = PyTuple_Size(object);
58 48 : if (tuple_size == 3) {
59 0 : unsigned long long pid;
60 0 : int task_id, vnn;
61 :
62 0 : if (!PyArg_ParseTuple(object, "Kii", &pid, &task_id, &vnn)) {
63 0 : return false;
64 : }
65 0 : server_id->pid = pid;
66 0 : server_id->task_id = task_id;
67 0 : server_id->vnn = vnn;
68 0 : return true;
69 48 : } else if (tuple_size == 2) {
70 0 : unsigned long long pid;
71 0 : int task_id;
72 6 : if (!PyArg_ParseTuple(object, "Ki", &pid, &task_id))
73 0 : return false;
74 6 : *server_id = cluster_id(pid, task_id);
75 6 : return true;
76 42 : } else if (tuple_size == 1) {
77 42 : unsigned long long pid = getpid();
78 0 : int task_id;
79 42 : if (!PyArg_ParseTuple(object, "i", &task_id))
80 0 : return false;
81 42 : *server_id = cluster_id(pid, task_id);
82 42 : return true;
83 : } else {
84 0 : PyErr_SetString(PyExc_ValueError, "Expected tuple containing one, two, or three elements");
85 0 : return false;
86 : }
87 : }
88 :
89 : typedef struct {
90 : PyObject_HEAD
91 : TALLOC_CTX *mem_ctx;
92 : struct imessaging_context *msg_ctx;
93 : } imessaging_Object;
94 :
95 79 : static PyObject *py_imessaging_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs)
96 : {
97 0 : struct tevent_context *ev;
98 79 : const char *kwnames[] = { "own_id", "lp_ctx", NULL };
99 79 : PyObject *own_id = Py_None;
100 79 : PyObject *py_lp_ctx = Py_None;
101 0 : imessaging_Object *ret;
102 0 : struct loadparm_context *lp_ctx;
103 :
104 79 : if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO",
105 : discard_const_p(char *, kwnames), &own_id, &py_lp_ctx)) {
106 0 : return NULL;
107 : }
108 :
109 79 : ret = PyObject_New(imessaging_Object, &imessaging_Type);
110 79 : if (ret == NULL)
111 0 : return NULL;
112 :
113 79 : ret->mem_ctx = talloc_new(NULL);
114 :
115 79 : lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp_ctx);
116 79 : if (lp_ctx == NULL) {
117 0 : PyErr_SetString(PyExc_RuntimeError, "unable to interpret loadparm_context");
118 0 : talloc_free(ret->mem_ctx);
119 0 : return NULL;
120 : }
121 :
122 79 : ev = s4_event_context_init(ret->mem_ctx);
123 :
124 79 : if (own_id != Py_None) {
125 0 : struct server_id server_id;
126 :
127 44 : if (!server_id_from_py(own_id, &server_id)) {
128 0 : talloc_free(ret->mem_ctx);
129 0 : return NULL;
130 : }
131 :
132 44 : ret->msg_ctx = imessaging_init(ret->mem_ctx,
133 : lp_ctx,
134 : server_id,
135 : ev);
136 : } else {
137 35 : ret->msg_ctx = imessaging_client_init(ret->mem_ctx,
138 : lp_ctx,
139 : ev);
140 : }
141 :
142 79 : if (ret->msg_ctx == NULL) {
143 0 : PyErr_SetString(PyExc_RuntimeError, "unable to create a messaging context");
144 0 : talloc_free(ret->mem_ctx);
145 0 : return NULL;
146 : }
147 :
148 79 : return (PyObject *)ret;
149 : }
150 :
151 79 : static void py_imessaging_dealloc(PyObject *self)
152 : {
153 79 : imessaging_Object *iface = (imessaging_Object *)self;
154 79 : talloc_free(iface->msg_ctx);
155 79 : self->ob_type->tp_free(self);
156 79 : }
157 :
158 8 : static PyObject *py_imessaging_send(PyObject *self, PyObject *args, PyObject *kwargs)
159 : {
160 8 : imessaging_Object *iface = (imessaging_Object *)self;
161 0 : uint32_t msg_type;
162 0 : DATA_BLOB data;
163 0 : PyObject *target;
164 0 : NTSTATUS status;
165 0 : struct server_id server;
166 8 : const char *kwnames[] = { "target", "msg_type", "data", NULL };
167 0 : Py_ssize_t length;
168 :
169 8 : if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIs#:send",
170 : discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &length)) {
171 :
172 0 : return NULL;
173 : }
174 :
175 8 : data.length = length;
176 :
177 8 : if (!server_id_from_py(target, &server))
178 0 : return NULL;
179 :
180 8 : status = imessaging_send(iface->msg_ctx, server, msg_type, &data);
181 8 : if (NT_STATUS_IS_ERR(status)) {
182 0 : PyErr_SetNTSTATUS(status);
183 0 : return NULL;
184 : }
185 :
186 8 : Py_RETURN_NONE;
187 : }
188 :
189 2519 : static void py_msg_callback_wrapper(struct imessaging_context *msg,
190 : void *private_data,
191 : uint32_t msg_type,
192 : struct server_id server_id,
193 : size_t num_fds,
194 : int *fds,
195 : DATA_BLOB *data)
196 : {
197 2519 : PyObject *py_server_id, *callback_and_tuple = (PyObject *)private_data;
198 0 : PyObject *callback, *py_private;
199 2519 : PyObject *result = NULL;
200 :
201 2519 : struct server_id *p_server_id = NULL;
202 :
203 2519 : if (num_fds != 0) {
204 0 : DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
205 0 : return;
206 : }
207 :
208 2519 : p_server_id = talloc(NULL, struct server_id);
209 2519 : if (!p_server_id) {
210 0 : PyErr_NoMemory();
211 0 : return;
212 : }
213 2519 : *p_server_id = server_id;
214 :
215 2519 : py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id);
216 2519 : talloc_unlink(NULL, p_server_id);
217 2519 : if (py_server_id == NULL) {
218 0 : return;
219 : }
220 :
221 2519 : if (!PyArg_ParseTuple(callback_and_tuple, "OO",
222 : &callback,
223 : &py_private)) {
224 0 : return;
225 : }
226 :
227 2519 : result = PyObject_CallFunction(callback, discard_const_p(char, "OiOs#"),
228 : py_private,
229 : msg_type,
230 : py_server_id,
231 : data->data, data->length);
232 2519 : Py_XDECREF(result);
233 : }
234 :
235 62 : static PyObject *py_imessaging_register(PyObject *self, PyObject *args, PyObject *kwargs)
236 : {
237 62 : imessaging_Object *iface = (imessaging_Object *)self;
238 62 : int msg_type = -1;
239 0 : PyObject *callback_and_context;
240 0 : NTSTATUS status;
241 62 : const char *kwnames[] = { "callback_and_context", "msg_type", NULL };
242 :
243 62 : if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:register",
244 : discard_const_p(char *, kwnames),
245 : &callback_and_context, &msg_type)) {
246 0 : return NULL;
247 : }
248 62 : if (!PyTuple_Check(callback_and_context)
249 62 : || PyTuple_Size(callback_and_context) != 2) {
250 0 : PyErr_SetString(PyExc_ValueError, "Expected tuple of size 2 for callback_and_context");
251 0 : return NULL;
252 : }
253 :
254 62 : Py_INCREF(callback_and_context);
255 :
256 62 : if (msg_type == -1) {
257 5 : uint32_t msg_type32 = msg_type;
258 5 : status = imessaging_register_tmp(iface->msg_ctx, callback_and_context,
259 : py_msg_callback_wrapper, &msg_type32);
260 5 : msg_type = msg_type32;
261 : } else {
262 57 : status = imessaging_register(iface->msg_ctx, callback_and_context,
263 : msg_type, py_msg_callback_wrapper);
264 : }
265 62 : if (NT_STATUS_IS_ERR(status)) {
266 0 : Py_DECREF(callback_and_context);
267 0 : PyErr_SetNTSTATUS(status);
268 0 : return NULL;
269 : }
270 :
271 62 : return PyLong_FromLong(msg_type);
272 : }
273 :
274 62 : static PyObject *py_imessaging_deregister(PyObject *self, PyObject *args, PyObject *kwargs)
275 : {
276 62 : imessaging_Object *iface = (imessaging_Object *)self;
277 62 : int msg_type = -1;
278 0 : PyObject *callback;
279 62 : const char *kwnames[] = { "callback", "msg_type", NULL };
280 0 : size_t removed;
281 :
282 62 : if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:deregister",
283 : discard_const_p(char *, kwnames), &callback, &msg_type)) {
284 0 : return NULL;
285 : }
286 :
287 62 : removed = imessaging_deregister(iface->msg_ctx, msg_type, callback);
288 124 : while (removed-- > 0) {
289 62 : Py_DECREF(callback);
290 : }
291 :
292 62 : Py_RETURN_NONE;
293 : }
294 :
295 7468 : static void simple_timer_handler(struct tevent_context *ev,
296 : struct tevent_timer *te,
297 : struct timeval current_time,
298 : void *private_data)
299 : {
300 7468 : return;
301 : }
302 :
303 13469 : static PyObject *py_imessaging_loop_once(PyObject *self, PyObject *args, PyObject *kwargs)
304 : {
305 13469 : imessaging_Object *iface = (imessaging_Object *)self;
306 0 : double offset;
307 0 : int seconds;
308 0 : struct timeval next_event;
309 13469 : struct tevent_timer *timer = NULL;
310 13469 : const char *kwnames[] = { "timeout", NULL };
311 :
312 13469 : TALLOC_CTX *frame = talloc_stackframe();
313 :
314 13469 : if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d",
315 : discard_const_p(char *, kwnames), &offset)) {
316 0 : TALLOC_FREE(frame);
317 0 : return NULL;
318 : }
319 :
320 13469 : if (offset != 0.0) {
321 13469 : seconds = offset;
322 13469 : offset -= seconds;
323 13469 : next_event = tevent_timeval_current_ofs(seconds, (int)(offset*1000000));
324 :
325 13469 : timer = tevent_add_timer(iface->msg_ctx->ev, frame, next_event, simple_timer_handler,
326 : NULL);
327 13469 : if (timer == NULL) {
328 0 : PyErr_NoMemory();
329 0 : TALLOC_FREE(frame);
330 0 : return NULL;
331 : }
332 : }
333 :
334 13469 : tevent_loop_once(iface->msg_ctx->ev);
335 :
336 13469 : TALLOC_FREE(frame);
337 :
338 13469 : Py_RETURN_NONE;
339 : }
340 :
341 58 : static PyObject *py_irpc_add_name(PyObject *self, PyObject *args)
342 : {
343 58 : imessaging_Object *iface = (imessaging_Object *)self;
344 0 : char *server_name;
345 0 : NTSTATUS status;
346 :
347 58 : if (!PyArg_ParseTuple(args, "s", &server_name)) {
348 0 : return NULL;
349 : }
350 :
351 58 : status = irpc_add_name(iface->msg_ctx, server_name);
352 58 : if (!NT_STATUS_IS_OK(status)) {
353 0 : PyErr_SetNTSTATUS(status);
354 0 : return NULL;
355 : }
356 :
357 58 : Py_RETURN_NONE;
358 : }
359 :
360 58 : static PyObject *py_irpc_remove_name(PyObject *self, PyObject *args)
361 : {
362 58 : imessaging_Object *iface = (imessaging_Object *)self;
363 0 : char *server_name;
364 :
365 58 : if (!PyArg_ParseTuple(args, "s", &server_name)) {
366 0 : return NULL;
367 : }
368 :
369 58 : irpc_remove_name(iface->msg_ctx, server_name);
370 :
371 58 : Py_RETURN_NONE;
372 : }
373 :
374 19 : static PyObject *py_irpc_servers_byname(PyObject *self, PyObject *args)
375 : {
376 19 : imessaging_Object *iface = (imessaging_Object *)self;
377 0 : char *server_name;
378 0 : unsigned i, num_ids;
379 0 : struct server_id *ids;
380 0 : PyObject *pylist;
381 19 : TALLOC_CTX *mem_ctx = talloc_new(NULL);
382 0 : NTSTATUS status;
383 :
384 19 : if (!mem_ctx) {
385 0 : PyErr_NoMemory();
386 0 : return NULL;
387 : }
388 :
389 19 : if (!PyArg_ParseTuple(args, "s", &server_name)) {
390 0 : TALLOC_FREE(mem_ctx);
391 0 : return NULL;
392 : }
393 :
394 19 : status = irpc_servers_byname(iface->msg_ctx, mem_ctx, server_name,
395 : &num_ids, &ids);
396 19 : if (!NT_STATUS_IS_OK(status)) {
397 3 : TALLOC_FREE(mem_ctx);
398 3 : PyErr_SetString(PyExc_KeyError, "No such name");
399 3 : return NULL;
400 : }
401 :
402 16 : pylist = PyList_New(num_ids);
403 16 : if (pylist == NULL) {
404 0 : TALLOC_FREE(mem_ctx);
405 0 : PyErr_NoMemory();
406 0 : return NULL;
407 : }
408 32 : for (i = 0; i < num_ids; i++) {
409 0 : PyObject *py_server_id;
410 16 : struct server_id *p_server_id = talloc(NULL, struct server_id);
411 16 : if (!p_server_id) {
412 0 : TALLOC_FREE(mem_ctx);
413 0 : PyErr_NoMemory();
414 0 : return NULL;
415 : }
416 16 : *p_server_id = ids[i];
417 :
418 16 : py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id);
419 16 : if (!py_server_id) {
420 0 : TALLOC_FREE(mem_ctx);
421 0 : return NULL;
422 : }
423 16 : PyList_SetItem(pylist, i, py_server_id);
424 16 : talloc_unlink(NULL, p_server_id);
425 : }
426 16 : TALLOC_FREE(mem_ctx);
427 16 : return pylist;
428 : }
429 :
430 222 : static PyObject *py_irpc_all_servers(PyObject *self,
431 : PyObject *Py_UNUSED(ignored))
432 : {
433 222 : imessaging_Object *iface = (imessaging_Object *)self;
434 0 : PyObject *pylist;
435 0 : int i;
436 0 : struct irpc_name_records *records;
437 222 : TALLOC_CTX *mem_ctx = talloc_new(NULL);
438 222 : if (!mem_ctx) {
439 0 : PyErr_NoMemory();
440 0 : return NULL;
441 : }
442 :
443 222 : records = irpc_all_servers(iface->msg_ctx, mem_ctx);
444 222 : if (records == NULL) {
445 0 : TALLOC_FREE(mem_ctx);
446 0 : PyErr_NoMemory();
447 0 : return NULL;
448 : }
449 :
450 222 : pylist = PyList_New(records->num_records);
451 222 : if (pylist == NULL) {
452 0 : TALLOC_FREE(mem_ctx);
453 0 : PyErr_NoMemory();
454 0 : return NULL;
455 : }
456 7327 : for (i = 0; i < records->num_records; i++) {
457 0 : PyObject *py_name_record
458 7105 : = py_return_ndr_struct("samba.dcerpc.irpc",
459 : "name_record",
460 7105 : records->names[i],
461 7105 : records->names[i]);
462 7105 : if (!py_name_record) {
463 0 : TALLOC_FREE(mem_ctx);
464 0 : return NULL;
465 : }
466 7105 : PyList_SetItem(pylist, i,
467 : py_name_record);
468 : }
469 222 : TALLOC_FREE(mem_ctx);
470 222 : return pylist;
471 : }
472 :
473 : static PyMethodDef py_imessaging_methods[] = {
474 : { "send", PY_DISCARD_FUNC_SIG(PyCFunction, py_imessaging_send),
475 : METH_VARARGS|METH_KEYWORDS,
476 : "S.send(target, msg_type, data) -> None\nSend a message" },
477 : { "register", PY_DISCARD_FUNC_SIG(PyCFunction, py_imessaging_register),
478 : METH_VARARGS|METH_KEYWORDS,
479 : "S.register((callback, context), msg_type=None) -> msg_type\nRegister a message handler. "
480 : "The callback and context must be supplied as a two-element tuple." },
481 : { "deregister", PY_DISCARD_FUNC_SIG(PyCFunction,
482 : py_imessaging_deregister),
483 : METH_VARARGS|METH_KEYWORDS,
484 : "S.deregister((callback, context), msg_type) -> None\nDeregister a message handler "
485 : "The callback and context must be supplied as the exact same two-element tuple "
486 : "as was used at registration time." },
487 : { "loop_once", PY_DISCARD_FUNC_SIG(PyCFunction,
488 : py_imessaging_loop_once),
489 : METH_VARARGS|METH_KEYWORDS,
490 : "S.loop_once(timeout) -> None\n"
491 : "Loop on the internal event context until we get an event "
492 : "(which might be a message calling the callback), "
493 : "timeout after timeout seconds (if not 0)" },
494 : { "irpc_add_name", (PyCFunction)py_irpc_add_name, METH_VARARGS,
495 : "S.irpc_add_name(name) -> None\n"
496 : "Add this context to the list of server_id values that "
497 : "are registered for a particular name" },
498 : { "irpc_remove_name", (PyCFunction)py_irpc_remove_name, METH_VARARGS,
499 : "S.irpc_remove_name(name) -> None\n"
500 : "Remove this context from the list of server_id values that "
501 : "are registered for a particular name" },
502 : { "irpc_servers_byname", (PyCFunction)py_irpc_servers_byname, METH_VARARGS,
503 : "S.irpc_servers_byname(name) -> list\nGet list of server_id values that are registered for a particular name" },
504 : { "irpc_all_servers", (PyCFunction)py_irpc_all_servers, METH_NOARGS,
505 : "S.irpc_all_servers() -> list\n"
506 : "Get list of all registered names and the associated server_id values" },
507 : { NULL, NULL, 0, NULL }
508 : };
509 :
510 2 : static PyObject *py_imessaging_server_id(PyObject *obj, void *closure)
511 : {
512 2 : imessaging_Object *iface = (imessaging_Object *)obj;
513 0 : PyObject *py_server_id;
514 2 : struct server_id server_id = imessaging_get_server_id(iface->msg_ctx);
515 2 : struct server_id *p_server_id = talloc(NULL, struct server_id);
516 2 : if (!p_server_id) {
517 0 : PyErr_NoMemory();
518 0 : return NULL;
519 : }
520 2 : *p_server_id = server_id;
521 :
522 2 : py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id);
523 2 : talloc_unlink(NULL, p_server_id);
524 :
525 2 : return py_server_id;
526 : }
527 :
528 : static PyGetSetDef py_imessaging_getset[] = {
529 : {
530 : .name = discard_const_p(char, "server_id"),
531 : .get = py_imessaging_server_id,
532 : .doc = discard_const_p(char, "local server id")
533 : },
534 : { .name = NULL },
535 : };
536 :
537 :
538 : PyTypeObject imessaging_Type = {
539 : PyVarObject_HEAD_INIT(NULL, 0)
540 : .tp_name = "messaging.Messaging",
541 : .tp_basicsize = sizeof(imessaging_Object),
542 : .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
543 : .tp_new = py_imessaging_connect,
544 : .tp_dealloc = py_imessaging_dealloc,
545 : .tp_methods = py_imessaging_methods,
546 : .tp_getset = py_imessaging_getset,
547 : .tp_doc = "Messaging(own_id=None, lp_ctx=None)\n" \
548 : "Create a new object that can be used to communicate with the peers in the specified messaging path.\n"
549 : };
550 :
551 : static struct PyModuleDef moduledef = {
552 : PyModuleDef_HEAD_INIT,
553 : .m_name = "messaging",
554 : .m_doc = "Internal RPC",
555 : .m_size = -1,
556 : .m_methods = NULL,
557 : };
558 :
559 53 : MODULE_INIT_FUNC(messaging)
560 : {
561 1 : PyObject *mod;
562 :
563 53 : if (PyType_Ready(&imessaging_Type) < 0)
564 0 : return NULL;
565 :
566 53 : mod = PyModule_Create(&moduledef);
567 53 : if (mod == NULL)
568 0 : return NULL;
569 :
570 47 : Py_INCREF((PyObject *)&imessaging_Type);
571 53 : PyModule_AddObject(mod, "Messaging", (PyObject *)&imessaging_Type);
572 53 : PyModule_AddObject(mod, "IRPC_CALL_TIMEOUT", PyLong_FromLong(IRPC_CALL_TIMEOUT));
573 53 : PyModule_AddObject(mod, "IRPC_CALL_TIMEOUT_INF", PyLong_FromLong(IRPC_CALL_TIMEOUT_INF));
574 :
575 53 : return mod;
576 : }
|