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"
25 #include "python/modules.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"); \
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"); \
47 dn = pyldb_Dn_AS_DN(py_ldb_dn);
49 static PyObject
*py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord
*records
,
52 PyObject
*py_dns_list
;
54 py_dns_list
= PyList_New(num_records
);
55 if (py_dns_list
== 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
);
67 static int py_dnsp_DnssrvRpcRecord_get_array(PyObject
*value
,
69 struct dnsp_DnssrvRpcRecord
**records
,
70 uint16_t *num_records
)
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
));
81 for (i
= 0; i
< PyList_GET_SIZE(value
); i
++) {
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) {
88 if (talloc_reference(mem_ctx
, pytalloc_get_mem_ctx(item
)) == NULL
) {
92 recs
[i
] = *(struct dnsp_DnssrvRpcRecord
*)pytalloc_get_ptr(item
);
95 *num_records
= PyList_GET_SIZE(value
);
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
;
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
),
120 &py_dns_partition
)) {
123 PyErr_LDB_OR_RAISE(py_ldb
, samdb
);
125 if (py_dns_partition
) {
126 PyErr_LDB_DN_OR_RAISE(py_dns_partition
,
130 frame
= talloc_stackframe();
132 status
= dns_common_zones(samdb
, frame
, dns_partition
,
134 if (!NT_STATUS_IS_OK(status
)) {
136 PyErr_SetNTSTATUS(status
);
140 werr
= dns_common_name2dn(samdb
, zones_list
, frame
, dns_name
, &dn
);
141 if (!W_ERROR_IS_OK(werr
)) {
143 PyErr_SetWERROR(werr
);
147 werr
= dns_common_lookup(samdb
,
153 if (!W_ERROR_IS_OK(werr
)) {
155 PyErr_SetWERROR(werr
);
159 ret
= py_dnsp_DnssrvRpcRecord_get_list(records
, num_records
);
160 pydn
= pyldb_Dn_FromDn(dn
);
162 result
= Py_BuildValue("(OO)", pydn
, ret
);
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
;
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
)) {
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");
190 dns_el
= pyldb_MessageElement_AsMessageElement(py_dns_el
);
192 frame
= talloc_stackframe();
194 werr
= dns_common_extract(samdb
, dns_el
,
198 if (!W_ERROR_IS_OK(werr
)) {
200 PyErr_SetWERROR(werr
);
204 ret
= py_dnsp_DnssrvRpcRecord_get_list(records
, num_records
);
209 static PyObject
*py_dsdb_dns_replace(PyObject
*self
, PyObject
*args
)
211 struct ldb_context
*samdb
;
212 PyObject
*py_ldb
, *py_dns_records
;
218 struct dns_server_zone
*zones_list
;
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
)) {
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
);
244 werr
= dns_common_name2dn(samdb
, zones_list
, frame
, dns_name
, &dn
);
245 if (!W_ERROR_IS_OK(werr
)) {
246 PyErr_SetWERROR(werr
);
251 ret
= py_dnsp_DnssrvRpcRecord_get_array(py_dns_records
,
253 &records
, &num_records
);
259 werr
= dns_common_replace(samdb
,
262 false, /* Not adding a record */
266 if (!W_ERROR_IS_OK(werr
)) {
267 PyErr_SetWERROR(werr
);
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
;
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
)) {
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
,
305 &records
, &num_records
);
311 werr
= dns_common_replace(samdb
,
314 false, /* Not adding a node */
318 if (!W_ERROR_IS_OK(werr
)) {
319 PyErr_SetWERROR(werr
);
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
;
339 if (!PyArg_ParseTuple(args
, "OO", &py_recs
[0], &py_recs
[1])) {
343 for (i
= 0; i
< 2; i
++) {
344 type_correct
= py_check_dcerpc_type(py_recs
[i
],
347 if (! type_correct
) {
348 PyErr_SetString(PyExc_ValueError
,
349 "DnssrvRpcRecord expected");
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
)
368 if (!PyArg_ParseTuple(args
, "L", <
)) {
374 /* time_t is presumably 32 bit here */
375 PyErr_SetString(PyExc_ValueError
, "Time out of range");
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
;
387 if (!PyArg_ParseTuple(args
, "K", ×tamp
)) {
391 if (timestamp
> UINT32_MAX
) {
392 PyErr_SetString(PyExc_ValueError
, "Time out of range");
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");
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
,
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
,
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.",
432 .m_methods
= py_dsdb_dns_methods
,
435 MODULE_INIT_FUNC(dsdb_dns
)
439 m
= PyModule_Create(&moduledef
);