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