s4:dlz make b9_has_soa check dc=@ node
[gd/samba-autobuild/.git] / source4 / dns_server / pydns.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Python DNS server wrapper
5
6    Copyright (C) 2015 Andrew Bartlett
7
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.
12
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.
17
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/>.
20 */
21
22 #include <Python.h>
23 #include "python/py3compat.h"
24 #include "includes.h"
25 #include <pyldb.h>
26 #include <pytalloc.h>
27 #include "dns_server/dnsserver_common.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "dsdb/common/util.h"
30 #include "librpc/gen_ndr/ndr_dnsp.h"
31 #include "librpc/rpc/pyrpc_util.h"
32
33 /* FIXME: These should be in a header file somewhere */
34 #define PyErr_LDB_OR_RAISE(py_ldb, ldb) \
35         if (!py_check_dcerpc_type(py_ldb, "ldb", "Ldb")) { \
36                 PyErr_SetString(PyExc_TypeError, "Ldb connection object required"); \
37                 return NULL; \
38         } \
39         ldb = pyldb_Ldb_AsLdbContext(py_ldb);
40
41 #define PyErr_LDB_DN_OR_RAISE(py_ldb_dn, dn) \
42         if (!py_check_dcerpc_type(py_ldb_dn, "ldb", "Dn")) { \
43                 PyErr_SetString(PyExc_TypeError, "ldb Dn object required"); \
44                 return NULL; \
45         } \
46         dn = pyldb_Dn_AsDn(py_ldb_dn);
47
48 static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
49                                                   uint16_t num_records)
50 {
51         PyObject *py_dns_list;
52         int i;
53         py_dns_list = PyList_New(num_records);
54         if (py_dns_list == NULL) {
55                 return NULL;
56         }
57         for (i = 0; i < num_records; i++) {
58                 PyObject *py_dns_record;
59                 py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
60                 PyList_SetItem(py_dns_list, i, py_dns_record);
61         }
62         return py_dns_list;
63 }
64
65 static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
66                                              TALLOC_CTX *mem_ctx,
67                                              struct dnsp_DnssrvRpcRecord **records,
68                                              uint16_t *num_records)
69 {
70         int i;
71         struct dnsp_DnssrvRpcRecord *recs;
72         PY_CHECK_TYPE(&PyList_Type, value, return -1;);
73         recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
74                             PyList_GET_SIZE(value));
75         if (recs == NULL) {
76                 PyErr_NoMemory();
77                 return -1;
78         }
79         for (i = 0; i < PyList_GET_SIZE(value); i++) {
80                 bool type_correct;
81                 PyObject *item = PyList_GET_ITEM(value, i);
82                 type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
83                 if (type_correct == false) {
84                         return -1;
85                 }
86                 if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
87                         PyErr_NoMemory();
88                         return -1;
89                 }
90                 recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
91         }
92         *records = recs;
93         *num_records = PyList_GET_SIZE(value);
94         return 0;
95 }
96
97 static PyObject *py_dsdb_dns_lookup(PyObject *self,
98                                     PyObject *args, PyObject *kwargs)
99 {
100         struct ldb_context *samdb;
101         PyObject *py_ldb, *ret, *pydn;
102         PyObject *py_dns_partition = NULL;
103         PyObject *result = NULL;
104         char *dns_name;
105         TALLOC_CTX *frame;
106         NTSTATUS status;
107         WERROR werr;
108         struct dns_server_zone *zones_list;
109         struct ldb_dn *dn, *dns_partition = NULL;
110         struct dnsp_DnssrvRpcRecord *records;
111         uint16_t num_records;
112         const char * const kwnames[] = { "ldb", "dns_name",
113                                          "dns_partition", NULL };
114
115         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
116                                          discard_const_p(char *, kwnames),
117                                          &py_ldb, &dns_name,
118                                          &py_dns_partition)) {
119                 return NULL;
120         }
121         PyErr_LDB_OR_RAISE(py_ldb, samdb);
122
123         if (py_dns_partition) {
124                 PyErr_LDB_DN_OR_RAISE(py_dns_partition,
125                                       dns_partition);
126         }
127
128         frame = talloc_stackframe();
129
130         status = dns_common_zones(samdb, frame, dns_partition,
131                                   &zones_list);
132         if (!NT_STATUS_IS_OK(status)) {
133                 talloc_free(frame);
134                 PyErr_SetNTSTATUS(status);
135                 return NULL;
136         }
137
138         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
139         if (!W_ERROR_IS_OK(werr)) {
140                 talloc_free(frame);
141                 PyErr_SetWERROR(werr);
142                 return NULL;
143         }
144
145         werr = dns_common_lookup(samdb,
146                                  frame,
147                                  dn,
148                                  &records,
149                                  &num_records,
150                                  NULL);
151         if (!W_ERROR_IS_OK(werr)) {
152                 talloc_free(frame);
153                 PyErr_SetWERROR(werr);
154                 return NULL;
155         }
156
157         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
158         pydn = pyldb_Dn_FromDn(dn);
159         talloc_free(frame);
160         result = Py_BuildValue("(OO)", pydn, ret);
161         Py_CLEAR(ret);
162         Py_CLEAR(pydn);
163         return result;
164 }
165
166 static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
167 {
168         struct ldb_context *samdb;
169         PyObject *py_dns_el, *ret;
170         PyObject *py_ldb = NULL;
171         TALLOC_CTX *frame;
172         WERROR werr;
173         struct ldb_message_element *dns_el;
174         struct dnsp_DnssrvRpcRecord *records;
175         uint16_t num_records;
176
177         if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
178                 return NULL;
179         }
180
181         PyErr_LDB_OR_RAISE(py_ldb, samdb);
182
183         if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
184                 PyErr_SetString(PyExc_TypeError,
185                                 "ldb MessageElement object required");
186                 return NULL;
187         }
188         dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
189
190         frame = talloc_stackframe();
191
192         werr = dns_common_extract(samdb, dns_el,
193                                   frame,
194                                   &records,
195                                   &num_records);
196         if (!W_ERROR_IS_OK(werr)) {
197                 talloc_free(frame);
198                 PyErr_SetWERROR(werr);
199                 return NULL;
200         }
201
202         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
203         talloc_free(frame);
204         return ret;
205 }
206
207 static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
208 {
209         struct ldb_context *samdb;
210         PyObject *py_ldb, *py_dns_records;
211         char *dns_name;
212         TALLOC_CTX *frame;
213         NTSTATUS status;
214         WERROR werr;
215         int ret;
216         struct dns_server_zone *zones_list;
217         struct ldb_dn *dn;
218         struct dnsp_DnssrvRpcRecord *records;
219         uint16_t num_records;
220
221         /*
222          * TODO: This is a shocking abuse, but matches what the
223          * internal DNS server does, it should be pushed into
224          * dns_common_replace()
225          */
226         static const int serial = 110;
227
228         if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
229                 return NULL;
230         }
231         PyErr_LDB_OR_RAISE(py_ldb, samdb);
232
233         frame = talloc_stackframe();
234
235         status = dns_common_zones(samdb, frame, NULL, &zones_list);
236         if (!NT_STATUS_IS_OK(status)) {
237                 PyErr_SetNTSTATUS(status);
238                 talloc_free(frame);
239                 return NULL;
240         }
241
242         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
243         if (!W_ERROR_IS_OK(werr)) {
244                 PyErr_SetWERROR(werr);
245                 talloc_free(frame);
246                 return NULL;
247         }
248
249         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
250                                                 frame,
251                                                 &records, &num_records);
252         if (ret != 0) {
253                 talloc_free(frame);
254                 return NULL;
255         }
256
257         werr = dns_common_replace(samdb,
258                                   frame,
259                                   dn,
260                                   false, /* Not adding a record */
261                                   serial,
262                                   records,
263                                   num_records);
264         if (!W_ERROR_IS_OK(werr)) {
265                 PyErr_SetWERROR(werr);
266                 talloc_free(frame);
267                 return NULL;
268         }
269
270         talloc_free(frame);
271         Py_RETURN_NONE;
272 }
273
274 static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
275 {
276         struct ldb_context *samdb;
277         PyObject *py_ldb, *py_dn, *py_dns_records;
278         TALLOC_CTX *frame;
279         WERROR werr;
280         int ret;
281         struct ldb_dn *dn;
282         struct dnsp_DnssrvRpcRecord *records;
283         uint16_t num_records;
284
285         /*
286          * TODO: This is a shocking abuse, but matches what the
287          * internal DNS server does, it should be pushed into
288          * dns_common_replace()
289          */
290         static const int serial = 110;
291
292         if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
293                 return NULL;
294         }
295         PyErr_LDB_OR_RAISE(py_ldb, samdb);
296
297         PyErr_LDB_DN_OR_RAISE(py_dn, dn);
298
299         frame = talloc_stackframe();
300
301         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
302                                                 frame,
303                                                 &records, &num_records);
304         if (ret != 0) {
305                 talloc_free(frame);
306                 return NULL;
307         }
308
309         werr = dns_common_replace(samdb,
310                                   frame,
311                                   dn,
312                                   false, /* Not adding a record */
313                                   serial,
314                                   records,
315                                   num_records);
316         if (!W_ERROR_IS_OK(werr)) {
317                 PyErr_SetWERROR(werr);
318                 talloc_free(frame);
319                 return NULL;
320         }
321
322         talloc_free(frame);
323
324         Py_RETURN_NONE;
325 }
326
327 static PyMethodDef py_dsdb_dns_methods[] = {
328
329         { "lookup", (PyCFunction)py_dsdb_dns_lookup,
330                 METH_VARARGS|METH_KEYWORDS,
331                 "Get the DNS database entries for a DNS name"},
332         { "replace", (PyCFunction)py_dsdb_dns_replace,
333                 METH_VARARGS, "Replace the DNS database entries for a DNS name"},
334         { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
335                 METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
336         { "extract", (PyCFunction)py_dsdb_dns_extract,
337                 METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
338         { NULL }
339 };
340
341 static struct PyModuleDef moduledef = {
342     PyModuleDef_HEAD_INIT,
343     .m_name = "dsdb_dns",
344     .m_doc = "Python bindings for the DNS objects in the directory service databases.",
345     .m_size = -1,
346     .m_methods = py_dsdb_dns_methods,
347 };
348
349 MODULE_INIT_FUNC(dsdb_dns)
350 {
351         PyObject *m;
352
353         m = PyModule_Create(&moduledef);
354
355         if (m == NULL)
356                 return NULL;
357
358         return m;
359 }