ctdb-tests: Update statd-callout tests to handle both modes
[samba4-gss.git] / source4 / dns_server / pydns.c
blob67f72979fbcc6091486dc41d2aa01426f7d76649
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 "lib/replace/system/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 static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
35 uint16_t num_records)
37 PyObject *py_dns_list;
38 int i;
39 py_dns_list = PyList_New(num_records);
40 if (py_dns_list == NULL) {
41 return NULL;
43 for (i = 0; i < num_records; i++) {
44 PyObject *py_dns_record;
45 py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
46 PyList_SetItem(py_dns_list, i, py_dns_record);
48 return py_dns_list;
52 static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
53 TALLOC_CTX *mem_ctx,
54 struct dnsp_DnssrvRpcRecord **records,
55 uint16_t *num_records)
57 int i;
58 struct dnsp_DnssrvRpcRecord *recs;
59 PY_CHECK_TYPE(&PyList_Type, value, return -1;);
60 recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
61 PyList_GET_SIZE(value));
62 if (recs == NULL) {
63 PyErr_NoMemory();
64 return -1;
66 for (i = 0; i < PyList_GET_SIZE(value); i++) {
67 bool type_correct;
68 PyObject *item = PyList_GET_ITEM(value, i);
69 type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
70 if (type_correct == false) {
71 return -1;
73 if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
74 PyErr_NoMemory();
75 return -1;
77 recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
79 *records = recs;
80 *num_records = PyList_GET_SIZE(value);
81 return 0;
84 static PyObject *py_dsdb_dns_lookup(PyObject *self,
85 PyObject *args, PyObject *kwargs)
87 struct ldb_context *samdb;
88 PyObject *py_ldb, *ret, *pydn;
89 PyObject *py_dns_partition = NULL;
90 PyObject *result = NULL;
91 char *dns_name;
92 TALLOC_CTX *frame;
93 NTSTATUS status;
94 WERROR werr;
95 struct dns_server_zone *zones_list;
96 struct ldb_dn *dn, *dns_partition = NULL;
97 struct dnsp_DnssrvRpcRecord *records;
98 uint16_t num_records;
99 const char * const kwnames[] = { "ldb", "dns_name",
100 "dns_partition", NULL };
102 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
103 discard_const_p(char *, kwnames),
104 &py_ldb, &dns_name,
105 &py_dns_partition)) {
106 return NULL;
108 PyErr_LDB_OR_RAISE(py_ldb, samdb);
110 if (py_dns_partition) {
111 PyErr_LDB_DN_OR_RAISE(py_dns_partition,
112 dns_partition);
115 frame = talloc_stackframe();
117 status = dns_common_zones(samdb, frame, dns_partition,
118 &zones_list);
119 if (!NT_STATUS_IS_OK(status)) {
120 talloc_free(frame);
121 PyErr_SetNTSTATUS(status);
122 return NULL;
125 werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
126 if (!W_ERROR_IS_OK(werr)) {
127 talloc_free(frame);
128 PyErr_SetWERROR(werr);
129 return NULL;
132 werr = dns_common_lookup(samdb,
133 frame,
135 &records,
136 &num_records,
137 NULL);
138 if (!W_ERROR_IS_OK(werr)) {
139 talloc_free(frame);
140 PyErr_SetWERROR(werr);
141 return NULL;
144 ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
145 pydn = pyldb_Dn_FromDn(dn, (PyLdbObject *)py_ldb);
146 talloc_free(frame);
147 result = Py_BuildValue("(OO)", pydn, ret);
148 Py_CLEAR(ret);
149 Py_CLEAR(pydn);
150 return result;
153 static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
155 struct ldb_context *samdb;
156 PyObject *py_dns_el, *ret;
157 PyObject *py_ldb = NULL;
158 TALLOC_CTX *frame;
159 WERROR werr;
160 struct ldb_message_element *dns_el;
161 struct dnsp_DnssrvRpcRecord *records;
162 uint16_t num_records;
164 if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
165 return NULL;
168 PyErr_LDB_OR_RAISE(py_ldb, samdb);
170 if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
171 PyErr_SetString(PyExc_TypeError,
172 "ldb MessageElement object required");
173 return NULL;
175 dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
177 frame = talloc_stackframe();
179 werr = dns_common_extract(samdb, dns_el,
180 frame,
181 &records,
182 &num_records);
183 if (!W_ERROR_IS_OK(werr)) {
184 talloc_free(frame);
185 PyErr_SetWERROR(werr);
186 return NULL;
189 ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
190 talloc_free(frame);
191 return ret;
194 static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
196 struct ldb_context *samdb;
197 PyObject *py_ldb, *py_dns_records;
198 char *dns_name;
199 TALLOC_CTX *frame;
200 NTSTATUS status;
201 WERROR werr;
202 int ret;
203 struct dns_server_zone *zones_list;
204 struct ldb_dn *dn;
205 struct dnsp_DnssrvRpcRecord *records;
206 uint16_t num_records;
209 * TODO: This is a shocking abuse, but matches what the
210 * internal DNS server does, it should be pushed into
211 * dns_common_replace()
213 static const int serial = 110;
215 if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
216 return NULL;
218 PyErr_LDB_OR_RAISE(py_ldb, samdb);
220 frame = talloc_stackframe();
222 status = dns_common_zones(samdb, frame, NULL, &zones_list);
223 if (!NT_STATUS_IS_OK(status)) {
224 PyErr_SetNTSTATUS(status);
225 talloc_free(frame);
226 return NULL;
229 werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
230 if (!W_ERROR_IS_OK(werr)) {
231 PyErr_SetWERROR(werr);
232 talloc_free(frame);
233 return NULL;
236 ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
237 frame,
238 &records, &num_records);
239 if (ret != 0) {
240 talloc_free(frame);
241 return NULL;
244 werr = dns_common_replace(samdb,
245 frame,
247 false, /* Not adding a record */
248 serial,
249 records,
250 num_records);
251 if (!W_ERROR_IS_OK(werr)) {
252 PyErr_SetWERROR(werr);
253 talloc_free(frame);
254 return NULL;
257 talloc_free(frame);
258 Py_RETURN_NONE;
261 static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
263 struct ldb_context *samdb;
264 PyObject *py_ldb, *py_dn, *py_dns_records;
265 TALLOC_CTX *frame;
266 WERROR werr;
267 int ret;
268 struct ldb_dn *dn;
269 struct dnsp_DnssrvRpcRecord *records;
270 uint16_t num_records;
273 * TODO: This is a shocking abuse, but matches what the
274 * internal DNS server does, it should be pushed into
275 * dns_common_replace()
277 static const int serial = 110;
279 if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
280 return NULL;
282 PyErr_LDB_OR_RAISE(py_ldb, samdb);
284 PyErr_LDB_DN_OR_RAISE(py_dn, dn);
286 frame = talloc_stackframe();
288 ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
289 frame,
290 &records, &num_records);
291 if (ret != 0) {
292 talloc_free(frame);
293 return NULL;
296 werr = dns_common_replace(samdb,
297 frame,
299 false, /* Not adding a node */
300 serial,
301 records,
302 num_records);
303 if (!W_ERROR_IS_OK(werr)) {
304 PyErr_SetWERROR(werr);
305 talloc_free(frame);
306 return NULL;
309 talloc_free(frame);
311 Py_RETURN_NONE;
315 static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
317 PyObject *py_recs[2];
318 struct dnsp_DnssrvRpcRecord *rec1;
319 struct dnsp_DnssrvRpcRecord *rec2;
320 size_t i;
321 bool type_correct;
322 bool match;
324 if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
325 return NULL;
328 for (i = 0; i < 2; i++) {
329 type_correct = py_check_dcerpc_type(py_recs[i],
330 "samba.dcerpc.dnsp",
331 "DnssrvRpcRecord");
332 if (! type_correct) {
333 PyErr_SetString(PyExc_ValueError,
334 "DnssrvRpcRecord expected");
335 return NULL;
339 rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
340 rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
342 match = dns_record_match(rec1, rec2);
343 return PyBool_FromLong(match);
347 static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
349 uint32_t timestamp;
350 time_t t;
351 long long lt;
353 if (!PyArg_ParseTuple(args, "L", &lt)) {
354 return NULL;
357 t = lt;
358 if (t != lt) {
359 /* time_t is presumably 32 bit here */
360 PyErr_SetString(PyExc_ValueError, "Time out of range");
361 return NULL;
363 timestamp = unix_to_dns_timestamp(t);
364 return Py_BuildValue("k", (unsigned long) timestamp);
367 static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
369 unsigned long long timestamp;
370 NTSTATUS status;
371 NTTIME nt;
372 if (!PyArg_ParseTuple(args, "K", &timestamp)) {
373 return NULL;
376 if (timestamp > UINT32_MAX) {
377 PyErr_SetString(PyExc_ValueError, "Time out of range");
378 return NULL;
380 status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
381 if (!NT_STATUS_IS_OK(status)) {
382 PyErr_SetString(PyExc_ValueError, "Time out of range");
383 return NULL;
385 return Py_BuildValue("L", (long long) nt);
389 static PyMethodDef py_dsdb_dns_methods[] = {
391 { "lookup", PY_DISCARD_FUNC_SIG(PyCFunction, py_dsdb_dns_lookup),
392 METH_VARARGS|METH_KEYWORDS,
393 "Get the DNS database entries for a DNS name"},
394 { "replace", (PyCFunction)py_dsdb_dns_replace,
395 METH_VARARGS, "Replace the DNS database entries for a DNS name"},
396 { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
397 METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
398 { "records_match", (PyCFunction)py_dsdb_dns_records_match,
399 METH_VARARGS|METH_KEYWORDS,
400 "Decide whether two records match, according to dns update rules"},
401 { "extract", (PyCFunction)py_dsdb_dns_extract,
402 METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
403 { "unix_to_dns_timestamp", (PyCFunction)py_dsdb_dns_unix_to_dns_timestamp,
404 METH_VARARGS,
405 "Convert a time.time() value to a dns timestamp (hours since 1601)"},
406 { "dns_timestamp_to_nt_time", (PyCFunction)py_dsdb_dns_timestamp_to_nt_time,
407 METH_VARARGS,
408 "Convert a dns timestamp to an NTTIME value"},
412 static struct PyModuleDef moduledef = {
413 PyModuleDef_HEAD_INIT,
414 .m_name = "dsdb_dns",
415 .m_doc = "Python bindings for the DNS objects in the directory service databases.",
416 .m_size = -1,
417 .m_methods = py_dsdb_dns_methods,
420 MODULE_INIT_FUNC(dsdb_dns)
422 PyObject *m;
424 m = PyModule_Create(&moduledef);
426 if (m == NULL)
427 return NULL;
429 return m;