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