summaryrefslogtreecommitdiffstats
path: root/source4/dns_server/pydns.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/dns_server/pydns.c')
-rw-r--r--source4/dns_server/pydns.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/source4/dns_server/pydns.c b/source4/dns_server/pydns.c
new file mode 100644
index 0000000..6e99ebd
--- /dev/null
+++ b/source4/dns_server/pydns.c
@@ -0,0 +1,445 @@
+/*
+ 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 "lib/replace/system/python.h"
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.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(PyExc_TypeError, "Ldb connection object required"); \
+ return NULL; \
+ } \
+ ldb = pyldb_Ldb_AsLdbContext(py_ldb);
+
+#define PyErr_LDB_DN_OR_RAISE(py_ldb_dn, dn) \
+ if (!py_check_dcerpc_type(py_ldb_dn, "ldb", "Dn")) { \
+ PyErr_SetString(PyExc_TypeError, "ldb Dn object required"); \
+ return NULL; \
+ } \
+ dn = pyldb_Dn_AS_DN(py_ldb_dn);
+
+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, PyObject *kwargs)
+{
+ struct ldb_context *samdb;
+ PyObject *py_ldb, *ret, *pydn;
+ PyObject *py_dns_partition = NULL;
+ PyObject *result = NULL;
+ char *dns_name;
+ TALLOC_CTX *frame;
+ NTSTATUS status;
+ WERROR werr;
+ struct dns_server_zone *zones_list;
+ struct ldb_dn *dn, *dns_partition = NULL;
+ struct dnsp_DnssrvRpcRecord *records;
+ uint16_t num_records;
+ const char * const kwnames[] = { "ldb", "dns_name",
+ "dns_partition", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
+ discard_const_p(char *, kwnames),
+ &py_ldb, &dns_name,
+ &py_dns_partition)) {
+ return NULL;
+ }
+ PyErr_LDB_OR_RAISE(py_ldb, samdb);
+
+ if (py_dns_partition) {
+ PyErr_LDB_DN_OR_RAISE(py_dns_partition,
+ dns_partition);
+ }
+
+ frame = talloc_stackframe();
+
+ status = dns_common_zones(samdb, frame, dns_partition,
+ &zones_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(frame);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(frame);
+ PyErr_SetWERROR(werr);
+ return NULL;
+ }
+
+ werr = dns_common_lookup(samdb,
+ frame,
+ dn,
+ &records,
+ &num_records,
+ NULL);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(frame);
+ PyErr_SetWERROR(werr);
+ return NULL;
+ }
+
+ ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
+ pydn = pyldb_Dn_FromDn(dn);
+ talloc_free(frame);
+ result = Py_BuildValue("(OO)", pydn, ret);
+ Py_CLEAR(ret);
+ Py_CLEAR(pydn);
+ return result;
+}
+
+static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
+{
+ struct ldb_context *samdb;
+ PyObject *py_dns_el, *ret;
+ PyObject *py_ldb = NULL;
+ TALLOC_CTX *frame;
+ WERROR werr;
+ struct ldb_message_element *dns_el;
+ struct dnsp_DnssrvRpcRecord *records;
+ uint16_t num_records;
+
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
+ return NULL;
+ }
+
+ PyErr_LDB_OR_RAISE(py_ldb, samdb);
+
+ if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
+ PyErr_SetString(PyExc_TypeError,
+ "ldb MessageElement object required");
+ return NULL;
+ }
+ dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
+
+ frame = talloc_stackframe();
+
+ werr = dns_common_extract(samdb, dns_el,
+ frame,
+ &records,
+ &num_records);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(frame);
+ PyErr_SetWERROR(werr);
+ return NULL;
+ }
+
+ ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
+ talloc_free(frame);
+ return ret;
+}
+
+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, NULL, &zones_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(frame);
+ return NULL;
+ }
+
+ werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_SetWERROR(werr);
+ talloc_free(frame);
+ return NULL;
+ }
+
+ ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
+ frame,
+ &records, &num_records);
+ if (ret != 0) {
+ talloc_free(frame);
+ 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);
+ talloc_free(frame);
+ return NULL;
+ }
+
+ talloc_free(frame);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
+{
+ struct ldb_context *samdb;
+ PyObject *py_ldb, *py_dn, *py_dns_records;
+ TALLOC_CTX *frame;
+ WERROR werr;
+ int ret;
+ 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, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
+ return NULL;
+ }
+ PyErr_LDB_OR_RAISE(py_ldb, samdb);
+
+ PyErr_LDB_DN_OR_RAISE(py_dn, dn);
+
+ frame = talloc_stackframe();
+
+ ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
+ frame,
+ &records, &num_records);
+ if (ret != 0) {
+ talloc_free(frame);
+ return NULL;
+ }
+
+ werr = dns_common_replace(samdb,
+ frame,
+ dn,
+ false, /* Not adding a node */
+ serial,
+ records,
+ num_records);
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_SetWERROR(werr);
+ talloc_free(frame);
+ return NULL;
+ }
+
+ talloc_free(frame);
+
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
+{
+ PyObject *py_recs[2];
+ struct dnsp_DnssrvRpcRecord *rec1;
+ struct dnsp_DnssrvRpcRecord *rec2;
+ size_t i;
+ bool type_correct;
+ bool match;
+
+ if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
+ return NULL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ type_correct = py_check_dcerpc_type(py_recs[i],
+ "samba.dcerpc.dnsp",
+ "DnssrvRpcRecord");
+ if (! type_correct) {
+ PyErr_SetString(PyExc_ValueError,
+ "DnssrvRpcRecord expected");
+ return NULL;
+ }
+ }
+
+ rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
+ rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
+
+ match = dns_record_match(rec1, rec2);
+ return PyBool_FromLong(match);
+}
+
+
+static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
+{
+ uint32_t timestamp;
+ time_t t;
+ long long lt;
+
+ if (!PyArg_ParseTuple(args, "L", &lt)) {
+ return NULL;
+ }
+
+ t = lt;
+ if (t != lt) {
+ /* time_t is presumably 32 bit here */
+ PyErr_SetString(PyExc_ValueError, "Time out of range");
+ return NULL;
+ }
+ timestamp = unix_to_dns_timestamp(t);
+ return Py_BuildValue("k", (unsigned long) timestamp);
+}
+
+static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
+{
+ unsigned long long timestamp;
+ NTSTATUS status;
+ NTTIME nt;
+ if (!PyArg_ParseTuple(args, "K", &timestamp)) {
+ return NULL;
+ }
+
+ if (timestamp > UINT32_MAX) {
+ PyErr_SetString(PyExc_ValueError, "Time out of range");
+ return NULL;
+ }
+ status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetString(PyExc_ValueError, "Time out of range");
+ return NULL;
+ }
+ return Py_BuildValue("L", (long long) nt);
+}
+
+
+static PyMethodDef py_dsdb_dns_methods[] = {
+
+ { "lookup", PY_DISCARD_FUNC_SIG(PyCFunction, py_dsdb_dns_lookup),
+ METH_VARARGS|METH_KEYWORDS,
+ "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"},
+ { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
+ METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
+ { "records_match", (PyCFunction)py_dsdb_dns_records_match,
+ METH_VARARGS|METH_KEYWORDS,
+ "Decide whether two records match, according to dns update rules"},
+ { "extract", (PyCFunction)py_dsdb_dns_extract,
+ METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
+ { "unix_to_dns_timestamp", (PyCFunction)py_dsdb_dns_unix_to_dns_timestamp,
+ METH_VARARGS,
+ "Convert a time.time() value to a dns timestamp (hours since 1601)"},
+ { "dns_timestamp_to_nt_time", (PyCFunction)py_dsdb_dns_timestamp_to_nt_time,
+ METH_VARARGS,
+ "Convert a dns timestamp to an NTTIME value"},
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "dsdb_dns",
+ .m_doc = "Python bindings for the DNS objects in the directory service databases.",
+ .m_size = -1,
+ .m_methods = py_dsdb_dns_methods,
+};
+
+MODULE_INIT_FUNC(dsdb_dns)
+{
+ PyObject *m;
+
+ m = PyModule_Create(&moduledef);
+
+ if (m == NULL)
+ return NULL;
+
+ return m;
+}