dns_server: Add a python module directly accessing DNS records in sam.ldb
authorAndrew Bartlett <abartlet@samba.org>
Tue, 22 Sep 2015 00:11:04 +0000 (12:11 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 26 Oct 2015 04:11:21 +0000 (05:11 +0100)
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
python/samba/samdb.py
source4/dns_server/pydns.c [new file with mode: 0644]
source4/dns_server/wscript_build

index 817fbdb697b468facbf6c18479b20f76c22845a8..7081e3da6e1d68961e84ddc66e19607a31799522 100644 (file)
@@ -27,7 +27,7 @@ import ldb
 import time
 import base64
 import os
-from samba import dsdb
+from samba import dsdb, dsdb_dns
 from samba.ndr import ndr_unpack, ndr_pack
 from samba.dcerpc import drsblobs, misc
 from samba.common import normalise_int32
@@ -921,3 +921,11 @@ accountExpires: %u
         '''get the server DN from the rootDSE'''
         res = self.search(base="", scope=ldb.SCOPE_BASE, attrs=["serverName"])
         return res[0]["serverName"][0]
+
+    def dns_lookup(self, dns_name):
+        '''Do a DNS lookup in the database, returns the NDR database structures'''
+        return dsdb_dns.lookup(self, dns_name)
+
+    def dns_replace(self, dns_name, new_records):
+        '''Do a DNS modification on the database, sets the NDR database structures'''
+        return dsdb_dns.replace(self, dns_name, new_records)
diff --git a/source4/dns_server/pydns.c b/source4/dns_server/pydns.c
new file mode 100644 (file)
index 0000000..1fad692
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Python DNS server wrapper
+
+   Copyright (C) 2015 Andrew Bartlett
+
+   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 "includes.h"
+#include <pyldb.h>
+#include <pytalloc.h>
+#include "dns_server/dnsserver_common.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "librpc/rpc/pyrpc_util.h"
+
+/* FIXME: These should be in a header file somewhere */
+#define PyErr_LDB_OR_RAISE(py_ldb, ldb) \
+       if (!py_check_dcerpc_type(py_ldb, "ldb", "Ldb")) { \
+               PyErr_SetString(py_ldb_get_exception(), "Ldb connection object required"); \
+               return NULL; \
+       } \
+       ldb = pyldb_Ldb_AsLdbContext(py_ldb);
+
+static PyObject *py_ldb_get_exception(void)
+{
+       PyObject *mod = PyImport_ImportModule("ldb");
+       if (mod == NULL)
+               return NULL;
+
+       return PyObject_GetAttrString(mod, "LdbError");
+}
+
+static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
+                                                 uint16_t num_records)
+{
+       PyObject *py_dns_list;
+       int i;
+       py_dns_list = PyList_New(num_records);
+       if (py_dns_list == NULL) {
+               return NULL;
+       }
+       for (i = 0; i < num_records; i++) {
+               PyObject *py_dns_record;
+               py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
+               PyList_SetItem(py_dns_list, i, py_dns_record);
+       }
+       return py_dns_list;
+}
+
+static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
+                                            TALLOC_CTX *mem_ctx,
+                                            struct dnsp_DnssrvRpcRecord **records,
+                                            uint16_t *num_records)
+{
+       int i;
+       struct dnsp_DnssrvRpcRecord *recs;
+       PY_CHECK_TYPE(&PyList_Type, value, return -1;);
+       recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
+                           PyList_GET_SIZE(value));
+       if (recs == NULL) {
+               PyErr_NoMemory();
+               return -1;
+       }
+       for (i = 0; i < PyList_GET_SIZE(value); i++) {
+               bool type_correct;
+               PyObject *item = PyList_GET_ITEM(value, i);
+               type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
+               if (type_correct == false) {
+                       return -1;
+               }
+               if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
+                       PyErr_NoMemory();
+                       return -1;
+               }
+               recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
+       }
+       *records = recs;
+       *num_records = PyList_GET_SIZE(value);
+       return 0;
+}
+
+static PyObject *py_dsdb_dns_lookup(PyObject *self, PyObject *args)
+{
+       struct ldb_context *samdb;
+       PyObject *py_ldb;
+       char *dns_name;
+       TALLOC_CTX *frame;
+       NTSTATUS status;
+       WERROR werr;
+       struct dns_server_zone *zones_list;
+       struct ldb_dn *dn;
+       struct dnsp_DnssrvRpcRecord *records;
+       uint16_t num_records;
+
+       if (!PyArg_ParseTuple(args, "Os", &py_ldb, &dns_name)) {
+               return NULL;
+       }
+       PyErr_LDB_OR_RAISE(py_ldb, samdb);
+
+       frame = talloc_stackframe();
+
+       status = dns_common_zones(samdb, frame, &zones_list);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
+       if (!W_ERROR_IS_OK(werr)) {
+               PyErr_SetWERROR(werr);
+               return NULL;
+       }
+
+       werr = dns_common_lookup(samdb,
+                                frame,
+                                dn,
+                                &records,
+                                &num_records,
+                                NULL);
+       if (!W_ERROR_IS_OK(werr)) {
+               PyErr_SetWERROR(werr);
+               return NULL;
+       }
+
+       return py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
+}
+
+static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
+{
+       struct ldb_context *samdb;
+       PyObject *py_ldb, *py_dns_records;
+       char *dns_name;
+       TALLOC_CTX *frame;
+       NTSTATUS status;
+       WERROR werr;
+       int ret;
+       struct dns_server_zone *zones_list;
+       struct ldb_dn *dn;
+       struct dnsp_DnssrvRpcRecord *records;
+       uint16_t num_records;
+
+       /*
+        * TODO: This is a shocking abuse, but matches what the
+        * internal DNS server does, it should be pushed into
+        * dns_common_replace()
+        */
+       static const int serial = 110;
+
+       if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
+               return NULL;
+       }
+       PyErr_LDB_OR_RAISE(py_ldb, samdb);
+
+       frame = talloc_stackframe();
+
+       status = dns_common_zones(samdb, frame, &zones_list);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
+       if (!W_ERROR_IS_OK(werr)) {
+               PyErr_SetWERROR(werr);
+               return NULL;
+       }
+
+       ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
+                                               frame,
+                                               &records, &num_records);
+       if (ret != 0) {
+               return NULL;
+       }
+
+       werr = dns_common_replace(samdb,
+                                 frame,
+                                 dn,
+                                 false, /* Not adding a record */
+                                 serial,
+                                 records,
+                                 num_records);
+       if (!W_ERROR_IS_OK(werr)) {
+               PyErr_SetWERROR(werr);
+               return NULL;
+       }
+
+       Py_RETURN_NONE;
+}
+
+static PyMethodDef py_dsdb_dns_methods[] = {
+
+       { "lookup", (PyCFunction)py_dsdb_dns_lookup,
+               METH_VARARGS, "Get the DNS database entries for a DNS name"},
+       { "replace", (PyCFunction)py_dsdb_dns_replace,
+               METH_VARARGS, "Replace the DNS database entries for a DNS name"},
+       { NULL }
+};
+
+void initdsdb_dns(void);
+
+void initdsdb_dns(void)
+{
+       PyObject *m;
+
+       m = Py_InitModule3("dsdb_dns", py_dsdb_dns_methods,
+                          "Python bindings for the DNS objects in the directory service databases.");
+       if (m == NULL)
+               return;
+}
index a4f0f0c54b30d5da9f28afcc39d4084aa6de2629..eedbd1da0febdccc0086dacaf1651df5603f6336 100644 (file)
@@ -53,3 +53,9 @@ bld.SAMBA_LIBRARY('dlz_bind9_for_torture',
                   private_library=True,
                   deps='samba-hostconfig samdb-common gensec popt dnsserver_common',
                   enabled=bld.AD_DC_BUILD_IS_ENABLED())
+
+
+bld.SAMBA_PYTHON('python_dsdb_dns',
+                source='pydns.c',
+                deps='samdb pyldb-util pyrpc_util dnsserver_common',
+                realname='samba/dsdb_dns.so')