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