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