Cleanup references to module objects returned from PyImport_ImportModule
[gd/samba-autobuild/.git] / source4 / librpc / rpc / pyrpc_util.c
index f3911eeb9ab6652e99faaa7c88ba6bda7889e341..29fd281f54daddac19925730c4777318a3a07bc2 100644 (file)
@@ -21,6 +21,7 @@
 */
 
 #include <Python.h>
+#include "python/py3compat.h"
 #include "includes.h"
 #include "librpc/rpc/pyrpc_util.h"
 #include "librpc/rpc/dcerpc.h"
 #include "param/pyparam.h"
 #include "auth/credentials/pycredentials.h"
 #include "lib/events/events.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
 
-#ifndef Py_TYPE /* Py_TYPE is only available on Python > 2.6 */
-#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)
-#endif
-
-bool py_check_dcerpc_type(PyObject *obj, const char *module, const char *typename)
+bool py_check_dcerpc_type(PyObject *obj, const char *module, const char *type_name)
 {
        PyObject *mod;
        PyTypeObject *type;
@@ -43,16 +42,16 @@ bool py_check_dcerpc_type(PyObject *obj, const char *module, const char *typenam
 
        if (mod == NULL) {
                PyErr_Format(PyExc_RuntimeError, "Unable to import %s to check type %s",
-                       module, typename);
-               return NULL;
+                       module, type_name);
+               return false;
        }
 
-       type = (PyTypeObject *)PyObject_GetAttrString(mod, typename);
+       type = (PyTypeObject *)PyObject_GetAttrString(mod, type_name);
        Py_DECREF(mod);
        if (type == NULL) {
                PyErr_Format(PyExc_RuntimeError, "Unable to find type %s in module %s",
-                       module, typename);
-               return NULL;
+                       module, type_name);
+               return false;
        }
 
        ret = PyObject_TypeCheck(obj, type);
@@ -60,101 +59,212 @@ bool py_check_dcerpc_type(PyObject *obj, const char *module, const char *typenam
 
        if (!ret)
                PyErr_Format(PyExc_TypeError, "Expected type %s.%s, got %s",
-                       module, typename, Py_TYPE(obj)->tp_name);
+                       module, type_name, Py_TYPE(obj)->tp_name);
 
        return ret;
 }
 
-PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs, const struct ndr_interface_table *table)
+/*
+  connect to a IRPC pipe from python
+ */
+static NTSTATUS pyrpc_irpc_connect(TALLOC_CTX *mem_ctx, const char *irpc_server,
+                                  const struct ndr_interface_table *table,
+                                  struct tevent_context *event_ctx,
+                                  struct loadparm_context *lp_ctx,
+                                  struct dcerpc_binding_handle **binding_handle)
+{
+       struct imessaging_context *msg;
+
+       msg = imessaging_client_init(mem_ctx, lp_ctx, event_ctx);
+       NT_STATUS_HAVE_NO_MEMORY(msg);
+
+       *binding_handle = irpc_binding_handle_by_name(mem_ctx, msg, irpc_server, table);
+       if (*binding_handle == NULL) {
+               talloc_free(msg);
+               return NT_STATUS_INVALID_PIPE_STATE;
+       }
+
+       /*
+        * Note: this allows nested event loops to happen,
+        * but as there's no top level event loop it's not that critical.
+        */
+       dcerpc_binding_handle_set_sync_ev(*binding_handle, event_ctx);
+
+       return NT_STATUS_OK;
+}
+
+PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs,
+                                         const struct ndr_interface_table *table)
 {
        dcerpc_InterfaceObject *ret;
        const char *binding_string;
-       struct cli_credentials *credentials;
-       struct loadparm_context *lp_ctx = NULL;
        PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None, *py_basis = Py_None;
-       TALLOC_CTX *mem_ctx = NULL;
-       struct tevent_context *event_ctx;
        NTSTATUS status;
-
+       unsigned int timeout = (unsigned int)-1;
        const char *kwnames[] = {
-               "binding", "lp_ctx", "credentials", "basis_connection", NULL
+               "binding", "lp_ctx", "credentials", "timeout", "basis_connection", NULL
        };
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO:samr", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &py_basis)) {
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOIO:samr", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &timeout, &py_basis)) {
                return NULL;
        }
 
-       mem_ctx = talloc_new(NULL);
-       if (mem_ctx == NULL) {
-               PyErr_NoMemory();
+       status = dcerpc_init();
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
                return NULL;
        }
 
-       lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
-       if (lp_ctx == NULL) {
-               PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
-               talloc_free(mem_ctx);
+       ret = PyObject_New(dcerpc_InterfaceObject, type);
+       if (ret == NULL) {
+               PyErr_NoMemory();
                return NULL;
        }
 
-       status = dcerpc_init(lp_ctx);
-       if (!NT_STATUS_IS_OK(status)) {
-               PyErr_SetNTSTATUS(status);
-               talloc_free(mem_ctx);
-               return NULL;
-       }
-       credentials = cli_credentials_from_py_object(py_credentials);
-       if (credentials == NULL) {
-               PyErr_SetString(PyExc_TypeError, "Expected credentials");
-               talloc_free(mem_ctx);
+       ret->pipe = NULL;
+       ret->binding_handle = NULL;
+       ret->mem_ctx = talloc_new(NULL);
+       if (ret->mem_ctx == NULL) {
+               PyErr_NoMemory();
                return NULL;
        }
-       ret = PyObject_New(dcerpc_InterfaceObject, type);
-       ret->mem_ctx = mem_ctx;
 
-       event_ctx = s4_event_context_init(ret->mem_ctx);
+       if (strncmp(binding_string, "irpc:", 5) == 0) {
+               struct tevent_context *event_ctx;
+               struct loadparm_context *lp_ctx;
 
-       if (py_basis != Py_None) {
+               event_ctx = s4_event_context_init(ret->mem_ctx);
+               if (event_ctx == NULL) {
+                       PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+
+               lp_ctx = lpcfg_from_py_object(event_ctx, py_lp_ctx);
+               if (lp_ctx == NULL) {
+                       PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+
+               status = pyrpc_irpc_connect(ret->mem_ctx, binding_string+5, table,
+                                           event_ctx, lp_ctx, &ret->binding_handle);
+               if (!NT_STATUS_IS_OK(status)) {
+                       PyErr_SetNTSTATUS(status);
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+       } else if (py_basis != Py_None) {
                struct dcerpc_pipe *base_pipe;
                PyObject *py_base;
                PyTypeObject *ClientConnection_Type;
 
                py_base = PyImport_ImportModule("samba.dcerpc.base");
                if (py_base == NULL) {
-                       talloc_free(mem_ctx);
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
                        return NULL;
                }
 
                ClientConnection_Type = (PyTypeObject *)PyObject_GetAttrString(py_base, "ClientConnection");
                if (ClientConnection_Type == NULL) {
                        PyErr_SetNone(PyExc_TypeError);
-                       talloc_free(mem_ctx);
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       Py_DECREF(py_base);
                        return NULL;
                }
 
                if (!PyObject_TypeCheck(py_basis, ClientConnection_Type)) {
                        PyErr_SetString(PyExc_TypeError, "basis_connection must be a DCE/RPC connection");
-                       talloc_free(mem_ctx);
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       Py_DECREF(py_base);
+                       Py_DECREF(ClientConnection_Type);
                        return NULL;
                }
 
-               base_pipe = talloc_reference(mem_ctx, ((dcerpc_InterfaceObject *)py_basis)->pipe);
+               base_pipe = talloc_reference(ret->mem_ctx,
+                                        ((dcerpc_InterfaceObject *)py_basis)->pipe);
+               if (base_pipe == NULL) {
+                       PyErr_NoMemory();
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       Py_DECREF(py_base);
+                       Py_DECREF(ClientConnection_Type);
+                       return NULL;
+               }
 
                status = dcerpc_secondary_context(base_pipe, &ret->pipe, table);
+               if (!NT_STATUS_IS_OK(status)) {
+                       PyErr_SetNTSTATUS(status);
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       Py_DECREF(py_base);
+                       Py_DECREF(ClientConnection_Type);
+                       return NULL;
+               }
 
                ret->pipe = talloc_steal(ret->mem_ctx, ret->pipe);
+               Py_XDECREF(ClientConnection_Type);
+               Py_XDECREF(py_base);
        } else {
-               status = dcerpc_pipe_connect(event_ctx, &ret->pipe, binding_string,
-                            table, credentials, event_ctx, lp_ctx);
+               struct tevent_context *event_ctx;
+               struct loadparm_context *lp_ctx;
+               struct cli_credentials *credentials;
+
+               event_ctx = s4_event_context_init(ret->mem_ctx);
+               if (event_ctx == NULL) {
+                       PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+
+               lp_ctx = lpcfg_from_py_object(event_ctx, py_lp_ctx);
+               if (lp_ctx == NULL) {
+                       PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+
+               credentials = cli_credentials_from_py_object(py_credentials);
+               if (credentials == NULL) {
+                       PyErr_SetString(PyExc_TypeError, "Expected credentials");
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+               status = dcerpc_pipe_connect(ret->mem_ctx, &ret->pipe, binding_string,
+                            table, credentials, event_ctx, lp_ctx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       PyErr_SetNTSTATUS(status);
+                       TALLOC_FREE(ret->mem_ctx);
+                       Py_DECREF(ret);
+                       return NULL;
+               }
+
+               /*
+                * the event context is cached under the connection,
+                * so let it be a child of it.
+                */
+               talloc_steal(ret->pipe->conn, event_ctx);
        }
-       if (NT_STATUS_IS_ERR(status)) {
-               PyErr_SetNTSTATUS(status);
-               talloc_free(mem_ctx);
-               return NULL;
+
+       if (ret->pipe) {
+               ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+               ret->binding_handle = ret->pipe->binding_handle;
+       }
+
+       /* reset timeout for the handle */
+       if ((timeout != ((unsigned int)-1)) && (ret->binding_handle != NULL)) {
+               dcerpc_binding_handle_set_timeout(ret->binding_handle, timeout);
        }
 
-       ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
-       ret->binding_handle = ret->pipe->binding_handle;
        return (PyObject *)ret;
 }
 
@@ -217,6 +327,9 @@ bool PyInterface_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpc
                PyObject *ret;
                struct wrapperbase *wb = (struct wrapperbase *)calloc(sizeof(struct wrapperbase), 1);
 
+               if (wb == NULL) {
+                       return false;
+               }
                wb->name = discard_const_p(char, mds[i].name);
                wb->flags = PyWrapperFlag_KEYWORDS;
                wb->wrapper = (wrapperfunc)py_dcerpc_call_wrapper;
@@ -231,6 +344,28 @@ bool PyInterface_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpc
        return true;
 }
 
+PyObject *py_dcerpc_syntax_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs,
+                                      const struct ndr_syntax_id *syntax)
+{
+       PyObject *ret;
+       struct ndr_syntax_id *obj;
+       const char *kwnames[] = { NULL };
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, ":abstract_syntax", discard_const_p(char *, kwnames))) {
+               return NULL;
+       }
+
+       ret = pytalloc_new(struct ndr_syntax_id, type);
+       if (ret == NULL) {
+               return NULL;
+       }
+
+       obj = pytalloc_get_type(ret, struct ndr_syntax_id);
+       *obj = *syntax;
+
+       return ret;
+}
+
 void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status)
 {
        if (p && NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
@@ -248,12 +383,16 @@ void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status)
 
   r_ctx is the context that is a parent of r. It will be referenced by
   the resulting python object
+
+  This MUST only be used by objects that are based on pytalloc_Object
+  otherwise the pytalloc_reference_ex() will fail.
  */
 PyObject *py_return_ndr_struct(const char *module_name, const char *type_name,
                               TALLOC_CTX *r_ctx, void *r)
 {
        PyTypeObject *py_type;
        PyObject *module;
+       PyObject *result = NULL;
 
        if (r == NULL) {
                Py_RETURN_NONE;
@@ -266,8 +405,106 @@ PyObject *py_return_ndr_struct(const char *module_name, const char *type_name,
 
        py_type = (PyTypeObject *)PyObject_GetAttrString(module, type_name);
        if (py_type == NULL) {
+               Py_DECREF(module);
+               return NULL;
+       }
+
+       result = pytalloc_reference_ex(py_type, r_ctx, r);
+       Py_CLEAR(module);
+       Py_CLEAR(py_type);
+       return result;
+}
+
+PyObject *PyString_FromStringOrNULL(const char *str)
+{
+       if (str == NULL) {
+               Py_RETURN_NONE;
+       }
+       return PyStr_FromString(str);
+}
+
+PyObject *pyrpc_import_union(PyTypeObject *type, TALLOC_CTX *mem_ctx, int level,
+                            const void *in, const char *typename)
+{
+       PyObject *mem_ctx_obj = NULL;
+       PyObject *in_obj = NULL;
+       PyObject *ret = NULL;
+
+       mem_ctx_obj = pytalloc_GenericObject_reference(mem_ctx);
+       if (mem_ctx_obj == NULL) {
+               return NULL;
+       }
+
+       in_obj = pytalloc_GenericObject_reference_ex(mem_ctx, discard_const(in));
+       if (in_obj == NULL) {
+               Py_XDECREF(mem_ctx_obj);
+               return NULL;
+       }
+
+       ret = PyObject_CallMethod((PyObject *)type,
+                                 discard_const_p(char, "__import__"),
+                                 discard_const_p(char, "OiO"),
+                                 mem_ctx_obj, level, in_obj);
+       Py_XDECREF(mem_ctx_obj);
+       Py_XDECREF(in_obj);
+       if (ret == NULL) {
+               return NULL;
+       }
+
+       return ret;
+}
+
+void *pyrpc_export_union(PyTypeObject *type, TALLOC_CTX *mem_ctx, int level,
+                        PyObject *in, const char *typename)
+{
+       PyObject *mem_ctx_obj = NULL;
+       PyObject *ret_obj = NULL;
+       void *ret = NULL;
+
+       mem_ctx_obj = pytalloc_GenericObject_reference(mem_ctx);
+       if (mem_ctx_obj == NULL) {
+               return NULL;
+       }
+
+       ret_obj = PyObject_CallMethod((PyObject *)type,
+                                     discard_const_p(char, "__export__"),
+                                     discard_const_p(char, "OiO"),
+                                     mem_ctx_obj, level, in);
+       Py_XDECREF(mem_ctx_obj);
+       if (ret_obj == NULL) {
+               return NULL;
+       }
+
+       ret = _pytalloc_get_type(ret_obj, typename);
+       Py_XDECREF(ret_obj);
+       return ret;
+}
+
+PyObject *py_dcerpc_ndr_pointer_deref(PyTypeObject *type, PyObject *obj)
+{
+       if (!PyObject_TypeCheck(obj, type)) {
+               PyErr_Format(PyExc_TypeError,
+                            "Expected type '%s' but got type '%s'",
+                            (type)->tp_name, Py_TYPE(obj)->tp_name);
+               return NULL;
+       }
+
+       return PyObject_GetAttrString(obj, discard_const_p(char, "value"));
+}
+
+PyObject *py_dcerpc_ndr_pointer_wrap(PyTypeObject *type, PyObject *obj)
+{
+       PyObject *args = NULL;
+       PyObject *ret_obj = NULL;
+
+       args = PyTuple_New(1);
+       if (args == NULL) {
                return NULL;
        }
+       Py_XINCREF(obj);
+       PyTuple_SetItem(args, 0, obj);
 
-       return py_talloc_reference_ex(py_type, r_ctx, r);
+       ret_obj = PyObject_Call((PyObject *)type, args, NULL);
+       Py_XDECREF(args);
+       return ret_obj;
 }