VERSION: Disable GIT_SNAPSHOT for the 4.17.9 release.
[Samba.git] / source4 / dns_server / pydns.c
blob7b83d7c49a11aa775b22a91a9936e2dc8b364a50
1 /*
2 Unix SMB/CIFS implementation.
4 Python DNS server wrapper
6 Copyright (C) 2015 Andrew Bartlett
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.
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.
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/>.
22 #include <Python.h>
23 #include "python/py3compat.h"
24 #include "includes.h"
25 #include "python/modules.h"
26 #include <pyldb.h>
27 #include <pytalloc.h>
28 #include "dns_server/dnsserver_common.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "dsdb/common/util.h"
31 #include "librpc/gen_ndr/ndr_dnsp.h"
32 #include "librpc/rpc/pyrpc_util.h"
34 /* FIXME: These should be in a header file somewhere */
35 #define PyErr_LDB_OR_RAISE(py_ldb, ldb) \
36 if (!py_check_dcerpc_type(py_ldb, "ldb", "Ldb")) { \
37 PyErr_SetString(PyExc_TypeError, "Ldb connection object required"); \
38 return NULL; \
39 } \
40 ldb = pyldb_Ldb_AsLdbContext(py_ldb);
42 #define PyErr_LDB_DN_OR_RAISE(py_ldb_dn, dn) \
43 if (!py_check_dcerpc_type(py_ldb_dn, "ldb", "Dn")) { \
44 PyErr_SetString(PyExc_TypeError, "ldb Dn object required"); \
45 return NULL; \
46 } \
47 dn = pyldb_Dn_AS_DN(py_ldb_dn);
49 static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
50 uint16_t num_records)
52 PyObject *py_dns_list;
53 int i;
54 py_dns_list = PyList_New(num_records);
55 if (py_dns_list == NULL) {
56 return NULL;
58 for (i = 0; i < num_records; i++) {
59 PyObject *py_dns_record;
60 py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
61 PyList_SetItem(py_dns_list, i, py_dns_record);
63 return py_dns_list;
67 static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
68 TALLOC_CTX *mem_ctx,
69 struct dnsp_DnssrvRpcRecord **records,
70 uint16_t *num_records)
72 int i;
73 struct dnsp_DnssrvRpcRecord *recs;
74 PY_CHECK_TYPE(&PyList_Type, value, return -1;);
75 recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
76 PyList_GET_SIZE(value));
77 if (recs == NULL) {
78 PyErr_NoMemory();
79 return -1;
81 for (i = 0; i < PyList_GET_SIZE(value); i++) {
82 bool type_correct;
83 PyObject *item = PyList_GET_ITEM(value, i);
84 type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
85 if (type_correct == false) {
86 return -1;
88 if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
89 PyErr_NoMemory();
90 return -1;
92 recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
94 *records = recs;
95 *num_records = PyList_GET_SIZE(value);
96 return 0;
99 static PyObject *py_dsdb_dns_lookup(PyObject *self,
100 PyObject *args, PyObject *kwargs)
102 struct ldb_context *samdb;
103 PyObject *py_ldb, *ret, *pydn;
104 PyObject *py_dns_partition = NULL;
105 PyObject *result = NULL;
106 char *dns_name;
107 TALLOC_CTX *frame;
108 NTSTATUS status;
109 WERROR werr;
110 struct dns_server_zone *zones_list;
111 struct ldb_dn *dn, *dns_partition = NULL;
112 struct dnsp_DnssrvRpcRecord *records;
113 uint16_t num_records;
114 const char * const kwnames[] = { "ldb", "dns_name",
115 "dns_partition", NULL };
117 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
118 discard_const_p(char *, kwnames),
119 &py_ldb, &dns_name,
120 &py_dns_partition)) {
121 return NULL;
123 PyErr_LDB_OR_RAISE(py_ldb, samdb);
125 if (py_dns_partition) {
126 PyErr_LDB_DN_OR_RAISE(py_dns_partition,
127 dns_partition);
130 frame = talloc_stackframe();
132 status = dns_common_zones(samdb, frame, dns_partition,
133 &zones_list);
134 if (!NT_STATUS_IS_OK(status)) {
135 talloc_free(frame);
136 PyErr_SetNTSTATUS(status);
137 return NULL;
140 werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
141 if (!W_ERROR_IS_OK(werr)) {
142 talloc_free(frame);
143 PyErr_SetWERROR(werr);
144 return NULL;
147 werr = dns_common_lookup(samdb,
148 frame,
150 &records,
151 &num_records,
152 NULL);
153 if (!W_ERROR_IS_OK(werr)) {
154 talloc_free(frame);
155 PyErr_SetWERROR(werr);
156 return NULL;
159 ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
160 pydn = pyldb_Dn_FromDn(dn);
161 talloc_free(frame);
162 result = Py_BuildValue("(OO)", pydn, ret);
163 Py_CLEAR(ret);
164 Py_CLEAR(pydn);
165 return result;
168 static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
170 struct ldb_context *samdb;
171 PyObject *py_dns_el, *ret;
172 PyObject *py_ldb = NULL;
173 TALLOC_CTX *frame;
174 WERROR werr;
175 struct ldb_message_element *dns_el;
176 struct dnsp_DnssrvRpcRecord *records;
177 uint16_t num_records;
179 if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
180 return NULL;
183 PyErr_LDB_OR_RAISE(py_ldb, samdb);
185 if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
186 PyErr_SetString(PyExc_TypeError,
187 "ldb MessageElement object required");
188 return NULL;
190 dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
192 frame = talloc_stackframe();
194 werr = dns_common_extract(samdb, dns_el,
195 frame,
196 &records,
197 &num_records);
198 if (!W_ERROR_IS_OK(werr)) {
199 talloc_free(frame);
200 PyErr_SetWERROR(werr);
201 return NULL;
204 ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
205 talloc_free(frame);
206 return ret;
209 static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
211 struct ldb_context *samdb;
212 PyObject *py_ldb, *py_dns_records;
213 char *dns_name;
214 TALLOC_CTX *frame;
215 NTSTATUS status;
216 WERROR werr;
217 int ret;
218 struct dns_server_zone *zones_list;
219 struct ldb_dn *dn;
220 struct dnsp_DnssrvRpcRecord *records;
221 uint16_t num_records;
224 * TODO: This is a shocking abuse, but matches what the
225 * internal DNS server does, it should be pushed into
226 * dns_common_replace()
228 static const int serial = 110;
230 if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
231 return NULL;
233 PyErr_LDB_OR_RAISE(py_ldb, samdb);
235 frame = talloc_stackframe();
237 status = dns_common_zones(samdb, frame, NULL, &zones_list);
238 if (!NT_STATUS_IS_OK(status)) {
239 PyErr_SetNTSTATUS(status);
240 talloc_free(frame);
241 return NULL;
244 werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
245 if (!W_ERROR_IS_OK(werr)) {
246 PyErr_SetWERROR(werr);
247 talloc_free(frame);
248 return NULL;
251 ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
252 frame,
253 &records, &num_records);
254 if (ret != 0) {
255 talloc_free(frame);
256 return NULL;
259 werr = dns_common_replace(samdb,
260 frame,
262 false, /* Not adding a record */
263 serial,
264 records,
265 num_records);
266 if (!W_ERROR_IS_OK(werr)) {
267 PyErr_SetWERROR(werr);
268 talloc_free(frame);
269 return NULL;
272 talloc_free(frame);
273 Py_RETURN_NONE;
276 static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
278 struct ldb_context *samdb;
279 PyObject *py_ldb, *py_dn, *py_dns_records;
280 TALLOC_CTX *frame;
281 WERROR werr;
282 int ret;
283 struct ldb_dn *dn;
284 struct dnsp_DnssrvRpcRecord *records;
285 uint16_t num_records;
288 * TODO: This is a shocking abuse, but matches what the
289 * internal DNS server does, it should be pushed into
290 * dns_common_replace()
292 static const int serial = 110;
294 if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
295 return NULL;
297 PyErr_LDB_OR_RAISE(py_ldb, samdb);
299 PyErr_LDB_DN_OR_RAISE(py_dn, dn);
301 frame = talloc_stackframe();
303 ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
304 frame,
305 &records, &num_records);
306 if (ret != 0) {
307 talloc_free(frame);
308 return NULL;
311 werr = dns_common_replace(samdb,
312 frame,
314 false, /* Not adding a node */
315 serial,
316 records,
317 num_records);
318 if (!W_ERROR_IS_OK(werr)) {
319 PyErr_SetWERROR(werr);
320 talloc_free(frame);
321 return NULL;
324 talloc_free(frame);
326 Py_RETURN_NONE;
330 static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
332 PyObject *py_recs[2];
333 struct dnsp_DnssrvRpcRecord *rec1;
334 struct dnsp_DnssrvRpcRecord *rec2;
335 size_t i;
336 bool type_correct;
337 bool match;
339 if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
340 return NULL;
343 for (i = 0; i < 2; i++) {
344 type_correct = py_check_dcerpc_type(py_recs[i],
345 "samba.dcerpc.dnsp",
346 "DnssrvRpcRecord");
347 if (! type_correct) {
348 PyErr_SetString(PyExc_ValueError,
349 "DnssrvRpcRecord expected");
350 return NULL;
354 rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
355 rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
357 match = dns_record_match(rec1, rec2);
358 return PyBool_FromLong(match);
362 static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
364 uint32_t timestamp;
365 time_t t;
366 long long lt;
368 if (!PyArg_ParseTuple(args, "L", &lt)) {
369 return NULL;
372 t = lt;
373 if (t != lt) {
374 /* time_t is presumably 32 bit here */
375 PyErr_SetString(PyExc_ValueError, "Time out of range");
376 return NULL;
378 timestamp = unix_to_dns_timestamp(t);
379 return Py_BuildValue("k", (unsigned long) timestamp);
382 static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
384 unsigned long long timestamp;
385 NTSTATUS status;
386 NTTIME nt;
387 if (!PyArg_ParseTuple(args, "K", &timestamp)) {
388 return NULL;
391 if (timestamp > UINT32_MAX) {
392 PyErr_SetString(PyExc_ValueError, "Time out of range");
393 return NULL;
395 status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
396 if (!NT_STATUS_IS_OK(status)) {
397 PyErr_SetString(PyExc_ValueError, "Time out of range");
398 return NULL;
400 return Py_BuildValue("L", (long long) nt);
404 static PyMethodDef py_dsdb_dns_methods[] = {
406 { "lookup", PY_DISCARD_FUNC_SIG(PyCFunction, py_dsdb_dns_lookup),
407 METH_VARARGS|METH_KEYWORDS,
408 "Get the DNS database entries for a DNS name"},
409 { "replace", (PyCFunction)py_dsdb_dns_replace,
410 METH_VARARGS, "Replace the DNS database entries for a DNS name"},
411 { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
412 METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
413 { "records_match", (PyCFunction)py_dsdb_dns_records_match,
414 METH_VARARGS|METH_KEYWORDS,
415 "Decide whether two records match, according to dns update rules"},
416 { "extract", (PyCFunction)py_dsdb_dns_extract,
417 METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
418 { "unix_to_dns_timestamp", (PyCFunction)py_dsdb_dns_unix_to_dns_timestamp,
419 METH_VARARGS,
420 "Convert a time.time() value to a dns timestamp (hours since 1601)"},
421 { "dns_timestamp_to_nt_time", (PyCFunction)py_dsdb_dns_timestamp_to_nt_time,
422 METH_VARARGS,
423 "Convert a dns timestamp to an NTTIME value"},
427 static struct PyModuleDef moduledef = {
428 PyModuleDef_HEAD_INIT,
429 .m_name = "dsdb_dns",
430 .m_doc = "Python bindings for the DNS objects in the directory service databases.",
431 .m_size = -1,
432 .m_methods = py_dsdb_dns_methods,
435 MODULE_INIT_FUNC(dsdb_dns)
437 PyObject *m;
439 m = PyModule_Create(&moduledef);
441 if (m == NULL)
442 return NULL;
444 return m;