summaryrefslogtreecommitdiff
path: root/source3/rpc_client
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2019-07-28 15:08:29 +0200
committerRalph Boehme <slow@samba.org>2019-10-09 14:35:28 +0000
commit213ca6e4cc981b4a9550042ce927c677883810be (patch)
tree5ca2dd96cb0cda860353cdc8ab9192c758284e44 /source3/rpc_client
parentb27d9afa29435b7af6335f228b8a315701b2607a (diff)
downloadsamba-213ca6e4cc981b4a9550042ce927c677883810be.tar.gz
s3: add mdscli Python bindings
Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Noel Power <noel.power@suse.com>
Diffstat (limited to 'source3/rpc_client')
-rw-r--r--source3/rpc_client/py_mdscli.c567
1 files changed, 567 insertions, 0 deletions
diff --git a/source3/rpc_client/py_mdscli.c b/source3/rpc_client/py_mdscli.c
new file mode 100644
index 00000000000..05dd0390cbd
--- /dev/null
+++ b/source3/rpc_client/py_mdscli.c
@@ -0,0 +1,567 @@
+/*
+ Python interface to cli_mdssvc
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include <pytalloc.h>
+#include "includes.h"
+#include "python/py3compat.h"
+#include "python/modules.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "librpc/rpc/rpc_common.h"
+#include "librpc/rpc/pyrpc_util.h"
+#include "rpc_client/cli_mdssvc.h"
+#include "rpc_client/cli_mdssvc_private.h"
+
+static PyObject *search_get_results(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char * const kwnames[] = {"pipe", NULL};
+ PyObject *pypipe = NULL;
+ PyObject *result = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ uint64_t *cnids = NULL;
+ size_t i;
+ size_t ncnids;
+ NTSTATUS status;
+ int ret;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "O",
+ discard_const_p(char *, kwnames),
+ &pypipe)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto out;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto out;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ search = pytalloc_get_type(self, struct mdscli_search_ctx);
+ if (search == NULL) {
+ goto out;
+ }
+
+ /*
+ * We must use the async send/recv versions in order to pass the correct
+ * tevent context, here and any other place we call mdscli_*
+ * functions. Using the sync version we would be polling a temporary
+ * event context, but unfortunately the s4 Python RPC bindings dispatch
+ * events through
+ *
+ * dcerpc_bh_raw_call_send()
+ * -> dcerpc_request_send()
+ * -> dcerpc_schedule_io_trigger()
+ * -> dcerpc_send_request()
+ * -> tstream_writev_queue_send()
+ *
+ * on an hardcoded event context allocated via
+ *
+ * py_dcerpc_interface_init_helper()
+ * -> dcerpc_pipe_connect()
+ */
+ req = mdscli_get_results_send(frame,
+ pipe->ev,
+ search);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto out;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto out;
+ }
+
+ status = mdscli_get_results_recv(req, frame, &cnids);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
+ {
+ PyErr_SetNTSTATUS(status);
+ goto out;
+ }
+ TALLOC_FREE(req);
+
+ result = Py_BuildValue("[]");
+
+ ncnids = talloc_array_length(cnids);
+ for (i = 0; i < ncnids; i++) {
+ char *path = NULL;
+ PyObject *pypath = NULL;
+
+ req = mdscli_get_path_send(frame,
+ pipe->ev,
+ search->mdscli_ctx,
+ cnids[i]);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+
+ status = mdscli_get_path_recv(req, frame, &path);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ pypath = PyUnicode_FromString(path);
+ if (pypath == NULL) {
+ PyErr_NoMemory();
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+
+ ret = PyList_Append(result, pypath);
+ Py_DECREF(pypath);
+ if (ret == -1) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "list append failed");
+ Py_DECREF(result);
+ result = NULL;
+ goto out;
+ }
+ }
+
+out:
+ talloc_free(frame);
+ return result;
+}
+
+static PyObject *search_close(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char * const kwnames[] = {"pipe", NULL};
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "O",
+ discard_const_p(char *, kwnames),
+ &pypipe)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ search = pytalloc_get_type(self, struct mdscli_search_ctx);
+ if (search == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_close_search_send(frame,
+ pipe->ev,
+ &search);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_close_search_recv(req);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES))
+ {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+ TALLOC_FREE(req);
+
+ talloc_free(frame);
+ Py_INCREF(Py_None);
+ return Py_None;
+
+fail:
+ talloc_free(frame);
+ return NULL;
+}
+
+static PyMethodDef search_methods[] = {
+ {
+ .ml_name = "get_results",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_get_results),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "",
+ },
+ {
+ .ml_name = "close",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, search_close),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "",
+ },
+ { NULL },
+};
+
+static PyObject *search_new(PyTypeObject *type,
+ PyObject *args,
+ PyObject *kwds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct mdscli_search_ctx *search = NULL;
+ PyObject *self = NULL;
+
+ search = talloc_zero(frame, struct mdscli_search_ctx);
+ if (search == NULL) {
+ PyErr_NoMemory();
+ talloc_free(frame);
+ return NULL;
+ }
+
+ self = pytalloc_steal(type, search);
+ talloc_free(frame);
+ return self;
+}
+
+static PyTypeObject search_type = {
+ .tp_name = "mdscli.ctx.search",
+ .tp_new = search_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = "search([....]) -> mdssvc client search context\n",
+ .tp_methods = search_methods,
+};
+
+static PyObject *conn_sharepath(PyObject *self,
+ PyObject *unused)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct mdscli_ctx *ctx = NULL;
+ char *sharepath = NULL;
+ PyObject *result = NULL;
+
+ ctx = pytalloc_get_type(self, struct mdscli_ctx);
+ if (ctx == NULL) {
+ goto fail;
+ }
+
+ sharepath = mdscli_get_basepath(frame, ctx);
+ if (sharepath == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ result = PyUnicode_FromString(sharepath);
+
+fail:
+ talloc_free(frame);
+ return result;
+}
+
+static PyObject *conn_search(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct mdscli_ctx *ctx = NULL;
+ PyObject *result = NULL;
+ char *query = NULL;
+ char *basepath = NULL;
+ struct tevent_req *req = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ const char * const kwnames[] = {
+ "pipe", "query", "basepath", NULL
+ };
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "Oss",
+ discard_const_p(char *, kwnames),
+ &pypipe,
+ &query,
+ &basepath)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ ctx = pytalloc_get_type(self, struct mdscli_ctx);
+ if (ctx == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_search_send(frame,
+ pipe->ev,
+ ctx,
+ query,
+ basepath,
+ false);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_search_recv(req, frame, &search);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ result = pytalloc_steal(&search_type, search);
+
+fail:
+ talloc_free(frame);
+ return result;
+}
+
+static PyObject *conn_disconnect(PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct mdscli_ctx *ctx = NULL;
+ struct tevent_req *req = NULL;
+ const char * const kwnames[] = {"pipe", NULL};
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "O",
+ discard_const_p(char *, kwnames),
+ &pypipe)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ ctx = pytalloc_get_type(self, struct mdscli_ctx);
+ if (ctx == NULL) {
+ goto fail;
+ }
+
+ req = mdscli_disconnect_send(frame, pipe->ev, ctx);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_disconnect_recv(req);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ talloc_free(frame);
+ Py_INCREF(Py_None);
+ return Py_None;
+
+fail:
+ talloc_free(frame);
+ return NULL;
+}
+
+static PyMethodDef conn_methods[] = {
+ {
+ .ml_name = "sharepath",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_sharepath),
+ .ml_flags = METH_NOARGS,
+ .ml_doc = "mdscli.conn.sharepath(...) -> get share basepath",
+ },
+ {
+ .ml_name = "search",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_search),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "mdscli.conn.search(...) -> run mdssvc query",
+ },
+ {
+ .ml_name = "disconnect",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, conn_disconnect),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "mdscli.conn.disconnect(...) -> disconnect",
+ },
+ { NULL },
+};
+
+static PyObject *conn_new(PyTypeObject *type,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char * const kwnames[] = { "pipe", "share", "mountpoint", NULL };
+ PyObject *pypipe = NULL;
+ dcerpc_InterfaceObject *pipe = NULL;
+ struct tevent_req *req = NULL;
+ char *share = NULL;
+ char *mountpoint = NULL;
+ struct mdscli_ctx *ctx = NULL;
+ PyObject *self = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "Oss",
+ discard_const_p(char *, kwnames),
+ &pypipe,
+ &share,
+ &mountpoint)) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to parse args");
+ goto fail;
+ }
+
+ ok = py_check_dcerpc_type(pypipe,
+ "samba.dcerpc.base",
+ "ClientConnection");
+ if (!ok) {
+ goto fail;
+ }
+
+ pipe = (dcerpc_InterfaceObject *)pypipe;
+
+ req = mdscli_connect_send(frame,
+ pipe->ev,
+ pipe->binding_handle,
+ share,
+ mountpoint);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, pipe->ev, &status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+
+ status = mdscli_connect_recv(req, frame, &ctx);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ self = pytalloc_steal(type, ctx);
+
+fail:
+ talloc_free(frame);
+ return self;
+}
+
+static PyTypeObject conn_type = {
+ .tp_name = "mdscli.conn",
+ .tp_new = conn_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = "conn([....]) -> mdssvc connection\n",
+ .tp_methods = conn_methods,
+};
+
+static PyMethodDef mdscli_methods[] = {
+ { NULL },
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "mdscli",
+ .m_doc = "RPC mdssvc client",
+ .m_size = -1,
+ .m_methods = mdscli_methods,
+};
+
+MODULE_INIT_FUNC(mdscli)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ PyObject *m = NULL;
+ int ret;
+
+ ret = pytalloc_BaseObject_PyType_Ready(&conn_type);
+ if (ret < 0) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ret = pytalloc_BaseObject_PyType_Ready(&search_type);
+ if (ret < 0) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ Py_INCREF(&conn_type);
+ PyModule_AddObject(m, "conn", (PyObject *)&conn_type);
+
+ Py_INCREF(&search_type);
+ PyModule_AddObject(m, "search", (PyObject *)&search_type);
+
+ TALLOC_FREE(frame);
+ return m;
+}