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 "includes.h" 23#include <Python.h> 24#include "scripting/python/modules.h" 25#include "libcli/util/pyerrors.h" 26#include "librpc/rpc/pyrpc.h" 27#include "lib/messaging/irpc.h" 28#include "lib/messaging/messaging.h" 29#include "lib/events/events.h" 30#include "cluster/cluster.h" 31#include "param/param.h" 32#include "param/pyparam.h" 33 34#ifndef Py_RETURN_NONE 35#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None 36#endif 37 38PyAPI_DATA(PyTypeObject) messaging_Type; 39PyAPI_DATA(PyTypeObject) irpc_ClientConnectionType; 40 41/* FIXME: This prototype should be in py_irpc.h, or shared otherwise */ 42extern const struct PyNdrRpcMethodDef py_ndr_irpc_methods[]; 43 44static bool server_id_from_py(PyObject *object, struct server_id *server_id) 45{ 46 if (!PyTuple_Check(object)) { 47 PyErr_SetString(PyExc_ValueError, "Expected tuple"); 48 return false; 49 } 50 51 if (PyTuple_Size(object) == 3) { 52 return PyArg_ParseTuple(object, "iii", &server_id->id, &server_id->id2, &server_id->node); 53 } else { 54 int id, id2; 55 if (!PyArg_ParseTuple(object, "ii", &id, &id2)) 56 return false; 57 *server_id = cluster_id(id, id2); 58 return true; 59 } 60} 61 62typedef struct { 63 PyObject_HEAD 64 TALLOC_CTX *mem_ctx; 65 struct messaging_context *msg_ctx; 66} messaging_Object; 67 68PyObject *py_messaging_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs) 69{ 70 struct tevent_context *ev; 71 const char *kwnames[] = { "own_id", "messaging_path", NULL }; 72 PyObject *own_id = Py_None; 73 const char *messaging_path = NULL; 74 messaging_Object *ret; 75 76 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oz:connect", 77 discard_const_p(char *, kwnames), &own_id, &messaging_path)) { 78 return NULL; 79 } 80 81 ret = PyObject_New(messaging_Object, &messaging_Type); 82 if (ret == NULL) 83 return NULL; 84 85 ret->mem_ctx = talloc_new(NULL); 86 87 ev = s4_event_context_init(ret->mem_ctx); 88 89 if (messaging_path == NULL) { 90 messaging_path = lp_messaging_path(ret->mem_ctx, 91 py_default_loadparm_context(ret->mem_ctx)); 92 } else { 93 messaging_path = talloc_strdup(ret->mem_ctx, messaging_path); 94 } 95 96 if (own_id != Py_None) { 97 struct server_id server_id; 98 99 if (!server_id_from_py(own_id, &server_id)) 100 return NULL; 101 102 ret->msg_ctx = messaging_init(ret->mem_ctx, 103 messaging_path, 104 server_id, 105 py_iconv_convenience(ret->mem_ctx), 106 ev); 107 } else { 108 ret->msg_ctx = messaging_client_init(ret->mem_ctx, 109 messaging_path, 110 py_iconv_convenience(ret->mem_ctx), 111 ev); 112 } 113 114 if (ret->msg_ctx == NULL) { 115 PyErr_SetString(PyExc_RuntimeError, "messaging_connect unable to create a messaging context"); 116 talloc_free(ret->mem_ctx); 117 return NULL; 118 } 119 120 return (PyObject *)ret; 121} 122 123static void py_messaging_dealloc(PyObject *self) 124{ 125 messaging_Object *iface = (messaging_Object *)self; 126 talloc_free(iface->msg_ctx); 127 PyObject_Del(self); 128} 129 130static PyObject *py_messaging_send(PyObject *self, PyObject *args, PyObject *kwargs) 131{ 132 messaging_Object *iface = (messaging_Object *)self; 133 uint32_t msg_type; 134 DATA_BLOB data; 135 PyObject *target; 136 NTSTATUS status; 137 struct server_id server; 138 const char *kwnames[] = { "target", "msg_type", "data", NULL }; 139 int length; 140 141 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Ois#|:send", 142 discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &length)) { 143 return NULL; 144 } 145 146 data.length = length; 147 148 if (!server_id_from_py(target, &server)) 149 return NULL; 150 151 status = messaging_send(iface->msg_ctx, server, msg_type, &data); 152 if (NT_STATUS_IS_ERR(status)) { 153 PyErr_SetNTSTATUS(status); 154 return NULL; 155 } 156 157 Py_RETURN_NONE; 158} 159 160static void py_msg_callback_wrapper(struct messaging_context *msg, void *private_data, 161 uint32_t msg_type, 162 struct server_id server_id, DATA_BLOB *data) 163{ 164 PyObject *callback = (PyObject *)private_data; 165 166 PyObject_CallFunction(callback, discard_const_p(char, "i(iii)s#"), msg_type, 167 server_id.id, server_id.id2, server_id.node, 168 data->data, data->length); 169} 170 171static PyObject *py_messaging_register(PyObject *self, PyObject *args, PyObject *kwargs) 172{ 173 messaging_Object *iface = (messaging_Object *)self; 174 int msg_type = -1; 175 PyObject *callback; 176 NTSTATUS status; 177 const char *kwnames[] = { "callback", "msg_type", NULL }; 178 179 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send", 180 discard_const_p(char *, kwnames), &callback, &msg_type)) { 181 return NULL; 182 } 183 184 Py_INCREF(callback); 185 186 if (msg_type == -1) { 187 uint32_t msg_type32 = msg_type; 188 status = messaging_register_tmp(iface->msg_ctx, callback, 189 py_msg_callback_wrapper, &msg_type32); 190 msg_type = msg_type32; 191 } else { 192 status = messaging_register(iface->msg_ctx, callback, 193 msg_type, py_msg_callback_wrapper); 194 } 195 if (NT_STATUS_IS_ERR(status)) { 196 PyErr_SetNTSTATUS(status); 197 return NULL; 198 } 199 200 return PyLong_FromLong(msg_type); 201} 202 203static PyObject *py_messaging_deregister(PyObject *self, PyObject *args, PyObject *kwargs) 204{ 205 messaging_Object *iface = (messaging_Object *)self; 206 int msg_type = -1; 207 PyObject *callback; 208 const char *kwnames[] = { "callback", "msg_type", NULL }; 209 210 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send", 211 discard_const_p(char *, kwnames), &callback, &msg_type)) { 212 return NULL; 213 } 214 215 messaging_deregister(iface->msg_ctx, msg_type, callback); 216 217 Py_DECREF(callback); 218 219 Py_RETURN_NONE; 220} 221 222static PyObject *py_messaging_add_name(PyObject *self, PyObject *args, PyObject *kwargs) 223{ 224 messaging_Object *iface = (messaging_Object *)self; 225 NTSTATUS status; 226 char *name; 227 const char *kwnames[] = { "name", NULL }; 228 229 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|:send", 230 discard_const_p(char *, kwnames), &name)) { 231 return NULL; 232 } 233 234 status = irpc_add_name(iface->msg_ctx, name); 235 if (NT_STATUS_IS_ERR(status)) { 236 PyErr_SetNTSTATUS(status); 237 return NULL; 238 } 239 240 Py_RETURN_NONE; 241} 242 243 244static PyObject *py_messaging_remove_name(PyObject *self, PyObject *args, PyObject *kwargs) 245{ 246 messaging_Object *iface = (messaging_Object *)self; 247 char *name; 248 const char *kwnames[] = { "name", NULL }; 249 250 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|:send", 251 discard_const_p(char *, kwnames), &name)) { 252 return NULL; 253 } 254 255 irpc_remove_name(iface->msg_ctx, name); 256 257 Py_RETURN_NONE; 258} 259 260static PyMethodDef py_messaging_methods[] = { 261 { "send", (PyCFunction)py_messaging_send, METH_VARARGS|METH_KEYWORDS, 262 "S.send(target, msg_type, data) -> None\nSend a message" }, 263 { "register", (PyCFunction)py_messaging_register, METH_VARARGS|METH_KEYWORDS, 264 "S.register(callback, msg_type=None) -> msg_type\nRegister a message handler" }, 265 { "deregister", (PyCFunction)py_messaging_deregister, METH_VARARGS|METH_KEYWORDS, 266 "S.deregister(callback, msg_type) -> None\nDeregister a message handler" }, 267 { "add_name", (PyCFunction)py_messaging_add_name, METH_VARARGS|METH_KEYWORDS, "S.add_name(name) -> None\nListen on another name" }, 268 { "remove_name", (PyCFunction)py_messaging_remove_name, METH_VARARGS|METH_KEYWORDS, "S.remove_name(name) -> None\nStop listening on a name" }, 269 { NULL, NULL, 0, NULL } 270}; 271 272static PyObject *py_messaging_server_id(PyObject *obj, void *closure) 273{ 274 messaging_Object *iface = (messaging_Object *)obj; 275 struct server_id server_id = messaging_get_server_id(iface->msg_ctx); 276 277 return Py_BuildValue("(iii)", server_id.id, server_id.id2, 278 server_id.node); 279} 280 281static PyGetSetDef py_messaging_getset[] = { 282 { discard_const_p(char, "server_id"), py_messaging_server_id, NULL, 283 discard_const_p(char, "local server id") }, 284 { NULL }, 285}; 286 287 288PyTypeObject messaging_Type = { 289 PyObject_HEAD_INIT(NULL) 0, 290 .tp_name = "irpc.Messaging", 291 .tp_basicsize = sizeof(messaging_Object), 292 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, 293 .tp_new = py_messaging_connect, 294 .tp_dealloc = py_messaging_dealloc, 295 .tp_methods = py_messaging_methods, 296 .tp_getset = py_messaging_getset, 297 .tp_doc = "Messaging(own_id=None, messaging_path=None)\n" \ 298 "Create a new object that can be used to communicate with the peers in the specified messaging path.\n" \ 299 "If no path is specified, the default path from smb.conf will be used." 300}; 301 302 303/* 304 state of a irpc 'connection' 305*/ 306typedef struct { 307 PyObject_HEAD 308 const char *server_name; 309 struct server_id *dest_ids; 310 struct messaging_context *msg_ctx; 311 TALLOC_CTX *mem_ctx; 312} irpc_ClientConnectionObject; 313 314/* 315 setup a context for talking to a irpc server 316 example: 317 status = irpc.connect("smb_server"); 318*/ 319 320PyObject *py_irpc_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs) 321{ 322 struct tevent_context *ev; 323 const char *kwnames[] = { "server", "own_id", "messaging_path", NULL }; 324 char *server; 325 const char *messaging_path = NULL; 326 PyObject *own_id = Py_None; 327 irpc_ClientConnectionObject *ret; 328 329 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|Oz:connect", 330 discard_const_p(char *, kwnames), &server, &own_id, &messaging_path)) { 331 return NULL; 332 } 333 334 ret = PyObject_New(irpc_ClientConnectionObject, &irpc_ClientConnectionType); 335 if (ret == NULL) 336 return NULL; 337 338 ret->mem_ctx = talloc_new(NULL); 339 340 ret->server_name = server; 341 342 ev = s4_event_context_init(ret->mem_ctx); 343 344 if (messaging_path == NULL) { 345 messaging_path = lp_messaging_path(ret->mem_ctx, 346 py_default_loadparm_context(ret->mem_ctx)); 347 } else { 348 messaging_path = talloc_strdup(ret->mem_ctx, messaging_path); 349 } 350 351 if (own_id != Py_None) { 352 struct server_id server_id; 353 354 if (!server_id_from_py(own_id, &server_id)) 355 return NULL; 356 357 ret->msg_ctx = messaging_init(ret->mem_ctx, 358 messaging_path, 359 server_id, 360 py_iconv_convenience(ret->mem_ctx), 361 ev); 362 } else { 363 ret->msg_ctx = messaging_client_init(ret->mem_ctx, 364 messaging_path, 365 py_iconv_convenience(ret->mem_ctx), 366 ev); 367 } 368 369 if (ret->msg_ctx == NULL) { 370 PyErr_SetString(PyExc_RuntimeError, "irpc_connect unable to create a messaging context"); 371 talloc_free(ret->mem_ctx); 372 return NULL; 373 } 374 375 ret->dest_ids = irpc_servers_byname(ret->msg_ctx, ret->mem_ctx, ret->server_name); 376 if (ret->dest_ids == NULL || ret->dest_ids[0].id == 0) { 377 talloc_free(ret->mem_ctx); 378 PyErr_SetNTSTATUS(NT_STATUS_OBJECT_NAME_NOT_FOUND); 379 return NULL; 380 } else { 381 return (PyObject *)ret; 382 } 383} 384 385typedef struct { 386 PyObject_HEAD 387 struct irpc_request **reqs; 388 int count; 389 int current; 390 TALLOC_CTX *mem_ctx; 391 py_data_unpack_fn unpack_fn; 392} irpc_ResultObject; 393 394 395static PyObject *irpc_result_next(irpc_ResultObject *iterator) 396{ 397 NTSTATUS status; 398 399 if (iterator->current >= iterator->count) { 400 PyErr_SetString(PyExc_StopIteration, "No more results"); 401 return NULL; 402 } 403 404 status = irpc_call_recv(iterator->reqs[iterator->current]); 405 iterator->current++; 406 if (!NT_STATUS_IS_OK(status)) { 407 PyErr_SetNTSTATUS(status); 408 return NULL; 409 } 410 411 return iterator->unpack_fn(iterator->reqs[iterator->current-1]->r); 412} 413 414static PyObject *irpc_result_len(irpc_ResultObject *self) 415{ 416 return PyLong_FromLong(self->count); 417} 418 419static PyMethodDef irpc_result_methods[] = { 420 { "__len__", (PyCFunction)irpc_result_len, METH_NOARGS, 421 "Number of elements returned"}, 422 { NULL } 423}; 424 425static void irpc_result_dealloc(PyObject *self) 426{ 427 talloc_free(((irpc_ResultObject *)self)->mem_ctx); 428 PyObject_Del(self); 429} 430 431PyTypeObject irpc_ResultIteratorType = { 432 PyObject_HEAD_INIT(NULL) 0, 433 .tp_name = "irpc.ResultIterator", 434 .tp_basicsize = sizeof(irpc_ResultObject), 435 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, 436 .tp_iternext = (iternextfunc)irpc_result_next, 437 .tp_iter = PyObject_SelfIter, 438 .tp_methods = irpc_result_methods, 439 .tp_dealloc = irpc_result_dealloc, 440}; 441 442static PyObject *py_irpc_call(irpc_ClientConnectionObject *p, struct PyNdrRpcMethodDef *method_def, PyObject *args, PyObject *kwargs) 443{ 444 void *ptr; 445 struct irpc_request **reqs; 446 int i, count; 447 NTSTATUS status; 448 TALLOC_CTX *mem_ctx = talloc_new(NULL); 449 irpc_ResultObject *ret; 450 451 /* allocate the C structure */ 452 ptr = talloc_zero_size(mem_ctx, method_def->table->calls[method_def->opnum].struct_size); 453 if (ptr == NULL) { 454 status = NT_STATUS_NO_MEMORY; 455 goto done; 456 } 457 458 /* convert the mpr object into a C structure */ 459 if (!method_def->pack_in_data(args, kwargs, ptr)) { 460 talloc_free(mem_ctx); 461 return NULL; 462 } 463 464 for (count=0;p->dest_ids[count].id;count++) /* noop */ ; 465 466 /* we need to make a call per server */ 467 reqs = talloc_array(mem_ctx, struct irpc_request *, count); 468 if (reqs == NULL) { 469 status = NT_STATUS_NO_MEMORY; 470 goto done; 471 } 472 473 /* make the actual calls */ 474 for (i=0;i<count;i++) { 475 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i], 476 method_def->table, method_def->opnum, ptr, ptr); 477 if (reqs[i] == NULL) { 478 status = NT_STATUS_NO_MEMORY; 479 goto done; 480 } 481 talloc_steal(reqs, reqs[i]); 482 } 483 484 ret = PyObject_New(irpc_ResultObject, &irpc_ResultIteratorType); 485 ret->mem_ctx = mem_ctx; 486 ret->reqs = reqs; 487 ret->count = count; 488 ret->current = 0; 489 ret->unpack_fn = method_def->unpack_out_data; 490 491 return (PyObject *)ret; 492done: 493 talloc_free(mem_ctx); 494 PyErr_SetNTSTATUS(status); 495 return NULL; 496} 497 498static PyObject *py_irpc_call_wrapper(PyObject *self, PyObject *args, void *wrapped, PyObject *kwargs) 499{ 500 irpc_ClientConnectionObject *iface = (irpc_ClientConnectionObject *)self; 501 struct PyNdrRpcMethodDef *md = wrapped; 502 503 return py_irpc_call(iface, md, args, kwargs); 504} 505 506static void py_irpc_dealloc(PyObject *self) 507{ 508 irpc_ClientConnectionObject *iface = (irpc_ClientConnectionObject *)self; 509 talloc_free(iface->mem_ctx); 510 PyObject_Del(self); 511} 512 513PyTypeObject irpc_ClientConnectionType = { 514 PyObject_HEAD_INIT(NULL) 0, 515 .tp_name = "irpc.ClientConnection", 516 .tp_basicsize = sizeof(irpc_ClientConnectionObject), 517 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, 518 .tp_new = py_irpc_connect, 519 .tp_dealloc = py_irpc_dealloc, 520 .tp_doc = "ClientConnection(server, own_id=None, messaging_path=None)\n" \ 521 "Create a new IRPC client connection to communicate with the servers in the specified path.\n" \ 522 "If no path is specified, the default path from smb.conf will be used." 523}; 524 525static bool irpc_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpcMethodDef *mds) 526{ 527 int i; 528 for (i = 0; mds[i].name; i++) { 529 PyObject *ret; 530 struct wrapperbase *wb = calloc(sizeof(struct wrapperbase), 1); 531 532 wb->name = discard_const_p(char, mds[i].name); 533 wb->flags = PyWrapperFlag_KEYWORDS; 534 wb->wrapper = (wrapperfunc)py_irpc_call_wrapper; 535 wb->doc = discard_const_p(char, mds[i].doc); 536 537 ret = PyDescr_NewWrapper(ifacetype, wb, discard_const_p(void, &mds[i])); 538 539 PyDict_SetItemString(ifacetype->tp_dict, mds[i].name, 540 (PyObject *)ret); 541 } 542 543 return true; 544} 545 546void initmessaging(void) 547{ 548 PyObject *mod; 549 PyObject *dep_irpc; 550 551 dep_irpc = PyImport_ImportModule("samba.dcerpc.irpc"); 552 if (dep_irpc == NULL) 553 return; 554 555 if (PyType_Ready(&irpc_ClientConnectionType) < 0) 556 return; 557 558 if (PyType_Ready(&messaging_Type) < 0) 559 return; 560 561 if (PyType_Ready(&irpc_ResultIteratorType) < 0) 562 return; 563 564 if (!irpc_AddNdrRpcMethods(&irpc_ClientConnectionType, py_ndr_irpc_methods)) 565 return; 566 567 mod = Py_InitModule3("messaging", NULL, "Internal RPC"); 568 if (mod == NULL) 569 return; 570 571 Py_INCREF((PyObject *)&irpc_ClientConnectionType); 572 PyModule_AddObject(mod, "ClientConnection", (PyObject *)&irpc_ClientConnectionType); 573 574 Py_INCREF((PyObject *)&messaging_Type); 575 PyModule_AddObject(mod, "Messaging", (PyObject *)&messaging_Type); 576} 577