LCOV - code coverage report
Current view: top level - source4/dns_server - pydns.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 141 214 65.9 %
Date: 2024-02-28 12:06:22 Functions: 9 10 90.0 %

          Line data    Source code
       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 "lib/replace/system/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         425 : static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
      50             :                                                   uint16_t num_records)
      51             : {
      52           4 :         PyObject *py_dns_list;
      53           4 :         int i;
      54         425 :         py_dns_list = PyList_New(num_records);
      55         425 :         if (py_dns_list == NULL) {
      56           0 :                 return NULL;
      57             :         }
      58        1681 :         for (i = 0; i < num_records; i++) {
      59           6 :                 PyObject *py_dns_record;
      60        1256 :                 py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
      61        1256 :                 PyList_SetItem(py_dns_list, i, py_dns_record);
      62             :         }
      63         421 :         return py_dns_list;
      64             : }
      65             : 
      66             : 
      67         840 : static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
      68             :                                              TALLOC_CTX *mem_ctx,
      69             :                                              struct dnsp_DnssrvRpcRecord **records,
      70             :                                              uint16_t *num_records)
      71             : {
      72          29 :         int i;
      73          29 :         struct dnsp_DnssrvRpcRecord *recs;
      74         840 :         PY_CHECK_TYPE(&PyList_Type, value, return -1;);
      75         840 :         recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
      76             :                             PyList_GET_SIZE(value));
      77         840 :         if (recs == NULL) {
      78           0 :                 PyErr_NoMemory();
      79           0 :                 return -1;
      80             :         }
      81         957 :         for (i = 0; i < PyList_GET_SIZE(value); i++) {
      82           4 :                 bool type_correct;
      83         117 :                 PyObject *item = PyList_GET_ITEM(value, i);
      84         117 :                 type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
      85         117 :                 if (type_correct == false) {
      86           0 :                         return -1;
      87             :                 }
      88         117 :                 if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
      89           0 :                         PyErr_NoMemory();
      90           0 :                         return -1;
      91             :                 }
      92         117 :                 recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
      93             :         }
      94         840 :         *records = recs;
      95         840 :         *num_records = PyList_GET_SIZE(value);
      96         840 :         return 0;
      97             : }
      98             : 
      99         447 : static PyObject *py_dsdb_dns_lookup(PyObject *self,
     100             :                                     PyObject *args, PyObject *kwargs)
     101             : {
     102           6 :         struct ldb_context *samdb;
     103           6 :         PyObject *py_ldb, *ret, *pydn;
     104         447 :         PyObject *py_dns_partition = NULL;
     105         447 :         PyObject *result = NULL;
     106           6 :         char *dns_name;
     107           6 :         TALLOC_CTX *frame;
     108           6 :         NTSTATUS status;
     109           6 :         WERROR werr;
     110           6 :         struct dns_server_zone *zones_list;
     111         447 :         struct ldb_dn *dn, *dns_partition = NULL;
     112           6 :         struct dnsp_DnssrvRpcRecord *records;
     113           6 :         uint16_t num_records;
     114         447 :         const char * const kwnames[] = { "ldb", "dns_name",
     115             :                                          "dns_partition", NULL };
     116             : 
     117         447 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
     118             :                                          discard_const_p(char *, kwnames),
     119             :                                          &py_ldb, &dns_name,
     120             :                                          &py_dns_partition)) {
     121           0 :                 return NULL;
     122             :         }
     123         447 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     124             : 
     125         447 :         if (py_dns_partition) {
     126          93 :                 PyErr_LDB_DN_OR_RAISE(py_dns_partition,
     127           6 :                                       dns_partition);
     128             :         }
     129             : 
     130         447 :         frame = talloc_stackframe();
     131             : 
     132         447 :         status = dns_common_zones(samdb, frame, dns_partition,
     133             :                                   &zones_list);
     134         447 :         if (!NT_STATUS_IS_OK(status)) {
     135           0 :                 talloc_free(frame);
     136           0 :                 PyErr_SetNTSTATUS(status);
     137           0 :                 return NULL;
     138             :         }
     139             : 
     140         447 :         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
     141         447 :         if (!W_ERROR_IS_OK(werr)) {
     142          12 :                 talloc_free(frame);
     143          12 :                 PyErr_SetWERROR(werr);
     144          12 :                 return NULL;
     145             :         }
     146             : 
     147         435 :         werr = dns_common_lookup(samdb,
     148             :                                  frame,
     149             :                                  dn,
     150             :                                  &records,
     151             :                                  &num_records,
     152             :                                  NULL);
     153         435 :         if (!W_ERROR_IS_OK(werr)) {
     154          10 :                 talloc_free(frame);
     155          10 :                 PyErr_SetWERROR(werr);
     156          10 :                 return NULL;
     157             :         }
     158             : 
     159         425 :         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
     160         425 :         pydn = pyldb_Dn_FromDn(dn);
     161         425 :         talloc_free(frame);
     162         425 :         result = Py_BuildValue("(OO)", pydn, ret);
     163         425 :         Py_CLEAR(ret);
     164         425 :         Py_CLEAR(pydn);
     165         421 :         return result;
     166             : }
     167             : 
     168           0 : static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
     169             : {
     170           0 :         struct ldb_context *samdb;
     171           0 :         PyObject *py_dns_el, *ret;
     172           0 :         PyObject *py_ldb = NULL;
     173           0 :         TALLOC_CTX *frame;
     174           0 :         WERROR werr;
     175           0 :         struct ldb_message_element *dns_el;
     176           0 :         struct dnsp_DnssrvRpcRecord *records;
     177           0 :         uint16_t num_records;
     178             : 
     179           0 :         if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
     180           0 :                 return NULL;
     181             :         }
     182             : 
     183           0 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     184             : 
     185           0 :         if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
     186           0 :                 PyErr_SetString(PyExc_TypeError,
     187             :                                 "ldb MessageElement object required");
     188           0 :                 return NULL;
     189             :         }
     190           0 :         dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
     191             : 
     192           0 :         frame = talloc_stackframe();
     193             : 
     194           0 :         werr = dns_common_extract(samdb, dns_el,
     195             :                                   frame,
     196             :                                   &records,
     197             :                                   &num_records);
     198           0 :         if (!W_ERROR_IS_OK(werr)) {
     199           0 :                 talloc_free(frame);
     200           0 :                 PyErr_SetWERROR(werr);
     201           0 :                 return NULL;
     202             :         }
     203             : 
     204           0 :         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
     205           0 :         talloc_free(frame);
     206           0 :         return ret;
     207             : }
     208             : 
     209         166 : static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
     210             : {
     211           4 :         struct ldb_context *samdb;
     212           4 :         PyObject *py_ldb, *py_dns_records;
     213           4 :         char *dns_name;
     214           4 :         TALLOC_CTX *frame;
     215           4 :         NTSTATUS status;
     216           4 :         WERROR werr;
     217           4 :         int ret;
     218           4 :         struct dns_server_zone *zones_list;
     219           4 :         struct ldb_dn *dn;
     220           4 :         struct dnsp_DnssrvRpcRecord *records;
     221           4 :         uint16_t num_records;
     222             : 
     223             :         /*
     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()
     227             :          */
     228           4 :         static const int serial = 110;
     229             : 
     230         166 :         if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
     231           0 :                 return NULL;
     232             :         }
     233         166 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     234             : 
     235         166 :         frame = talloc_stackframe();
     236             : 
     237         166 :         status = dns_common_zones(samdb, frame, NULL, &zones_list);
     238         166 :         if (!NT_STATUS_IS_OK(status)) {
     239           0 :                 PyErr_SetNTSTATUS(status);
     240           0 :                 talloc_free(frame);
     241           0 :                 return NULL;
     242             :         }
     243             : 
     244         166 :         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
     245         166 :         if (!W_ERROR_IS_OK(werr)) {
     246           0 :                 PyErr_SetWERROR(werr);
     247           0 :                 talloc_free(frame);
     248           0 :                 return NULL;
     249             :         }
     250             : 
     251         166 :         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
     252             :                                                 frame,
     253             :                                                 &records, &num_records);
     254         166 :         if (ret != 0) {
     255           0 :                 talloc_free(frame);
     256           0 :                 return NULL;
     257             :         }
     258             : 
     259         166 :         werr = dns_common_replace(samdb,
     260             :                                   frame,
     261             :                                   dn,
     262             :                                   false, /* Not adding a record */
     263             :                                   serial,
     264             :                                   records,
     265             :                                   num_records);
     266         166 :         if (!W_ERROR_IS_OK(werr)) {
     267           0 :                 PyErr_SetWERROR(werr);
     268           0 :                 talloc_free(frame);
     269           0 :                 return NULL;
     270             :         }
     271             : 
     272         166 :         talloc_free(frame);
     273         166 :         Py_RETURN_NONE;
     274             : }
     275             : 
     276         674 : static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
     277             : {
     278          25 :         struct ldb_context *samdb;
     279          25 :         PyObject *py_ldb, *py_dn, *py_dns_records;
     280          25 :         TALLOC_CTX *frame;
     281          25 :         WERROR werr;
     282          25 :         int ret;
     283          25 :         struct ldb_dn *dn;
     284          25 :         struct dnsp_DnssrvRpcRecord *records;
     285          25 :         uint16_t num_records;
     286             : 
     287             :         /*
     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()
     291             :          */
     292          25 :         static const int serial = 110;
     293             : 
     294         674 :         if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
     295           0 :                 return NULL;
     296             :         }
     297         674 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     298             : 
     299         674 :         PyErr_LDB_DN_OR_RAISE(py_dn, dn);
     300             : 
     301         674 :         frame = talloc_stackframe();
     302             : 
     303         674 :         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
     304             :                                                 frame,
     305             :                                                 &records, &num_records);
     306         674 :         if (ret != 0) {
     307           0 :                 talloc_free(frame);
     308           0 :                 return NULL;
     309             :         }
     310             : 
     311         674 :         werr = dns_common_replace(samdb,
     312             :                                   frame,
     313             :                                   dn,
     314             :                                   false, /* Not adding a node */
     315             :                                   serial,
     316             :                                   records,
     317             :                                   num_records);
     318         674 :         if (!W_ERROR_IS_OK(werr)) {
     319           0 :                 PyErr_SetWERROR(werr);
     320           0 :                 talloc_free(frame);
     321           0 :                 return NULL;
     322             :         }
     323             : 
     324         674 :         talloc_free(frame);
     325             : 
     326         674 :         Py_RETURN_NONE;
     327             : }
     328             : 
     329             : 
     330        1458 : static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
     331             : {
     332           0 :         PyObject *py_recs[2];
     333           0 :         struct dnsp_DnssrvRpcRecord *rec1;
     334           0 :         struct dnsp_DnssrvRpcRecord *rec2;
     335           0 :         size_t i;
     336           0 :         bool type_correct;
     337           0 :         bool match;
     338             : 
     339        1458 :         if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
     340           0 :                 return NULL;
     341             :         }
     342             : 
     343        4374 :         for (i = 0; i < 2; i++) {
     344        2916 :                 type_correct = py_check_dcerpc_type(py_recs[i],
     345             :                                                     "samba.dcerpc.dnsp",
     346             :                                                     "DnssrvRpcRecord");
     347        2916 :                 if (! type_correct) {
     348           0 :                         PyErr_SetString(PyExc_ValueError,
     349             :                                         "DnssrvRpcRecord expected");
     350           0 :                         return NULL;
     351             :                 }
     352             :         }
     353             : 
     354        1458 :         rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
     355        1458 :         rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
     356             : 
     357        1458 :         match = dns_record_match(rec1, rec2);
     358        1458 :         return PyBool_FromLong(match);
     359             : }
     360             : 
     361             : 
     362         207 : static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
     363             : {
     364           0 :         uint32_t timestamp;
     365           0 :         time_t t;
     366           0 :         long long lt;
     367             : 
     368         207 :         if (!PyArg_ParseTuple(args, "L", &lt)) {
     369           0 :                 return NULL;
     370             :         }
     371             : 
     372         207 :         t = lt;
     373         207 :         if (t != lt) {
     374             :                 /* time_t is presumably 32 bit here */
     375           0 :                 PyErr_SetString(PyExc_ValueError, "Time out of range");
     376           0 :                 return NULL;
     377             :         }
     378         207 :         timestamp = unix_to_dns_timestamp(t);
     379         207 :         return Py_BuildValue("k", (unsigned long) timestamp);
     380             : }
     381             : 
     382          11 : static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
     383             : {
     384           0 :         unsigned long long timestamp;
     385           0 :         NTSTATUS status;
     386           0 :         NTTIME nt;
     387          11 :         if (!PyArg_ParseTuple(args, "K", &timestamp)) {
     388           0 :                 return NULL;
     389             :         }
     390             : 
     391          11 :         if (timestamp > UINT32_MAX) {
     392           1 :                 PyErr_SetString(PyExc_ValueError, "Time out of range");
     393           1 :                 return NULL;
     394             :         }
     395          10 :         status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
     396          10 :         if (!NT_STATUS_IS_OK(status)) {
     397           2 :                 PyErr_SetString(PyExc_ValueError, "Time out of range");
     398           2 :                 return NULL;
     399             :         }
     400           8 :         return Py_BuildValue("L", (long long) nt);
     401             : }
     402             : 
     403             : 
     404             : static PyMethodDef py_dsdb_dns_methods[] = {
     405             : 
     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,
     419             :           METH_VARARGS,
     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,
     422             :           METH_VARARGS,
     423             :           "Convert a dns timestamp to an NTTIME value"},
     424             :         {0}
     425             : };
     426             : 
     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.",
     431             :     .m_size = -1,
     432             :     .m_methods = py_dsdb_dns_methods,
     433             : };
     434             : 
     435        7886 : MODULE_INIT_FUNC(dsdb_dns)
     436             : {
     437         192 :         PyObject *m;
     438             : 
     439        7886 :         m = PyModule_Create(&moduledef);
     440             : 
     441        7886 :         if (m == NULL)
     442           0 :                 return NULL;
     443             : 
     444        7694 :         return m;
     445             : }

Generated by: LCOV version 1.14