LCOV - code coverage report
Current view: top level - source4/dns_server - pydns.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 140 213 65.7 %
Date: 2024-04-21 15:09:00 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         430 : static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
      35             :                                                   uint16_t num_records)
      36             : {
      37           4 :         PyObject *py_dns_list;
      38           4 :         int i;
      39         430 :         py_dns_list = PyList_New(num_records);
      40         430 :         if (py_dns_list == NULL) {
      41           0 :                 return NULL;
      42             :         }
      43        1701 :         for (i = 0; i < num_records; i++) {
      44           6 :                 PyObject *py_dns_record;
      45        1271 :                 py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
      46        1271 :                 PyList_SetItem(py_dns_list, i, py_dns_record);
      47             :         }
      48         426 :         return py_dns_list;
      49             : }
      50             : 
      51             : 
      52         842 : static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
      53             :                                              TALLOC_CTX *mem_ctx,
      54             :                                              struct dnsp_DnssrvRpcRecord **records,
      55             :                                              uint16_t *num_records)
      56             : {
      57          29 :         int i;
      58          29 :         struct dnsp_DnssrvRpcRecord *recs;
      59         842 :         PY_CHECK_TYPE(&PyList_Type, value, return -1;);
      60         842 :         recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
      61             :                             PyList_GET_SIZE(value));
      62         842 :         if (recs == NULL) {
      63           0 :                 PyErr_NoMemory();
      64           0 :                 return -1;
      65             :         }
      66         959 :         for (i = 0; i < PyList_GET_SIZE(value); i++) {
      67           4 :                 bool type_correct;
      68         117 :                 PyObject *item = PyList_GET_ITEM(value, i);
      69         117 :                 type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
      70         117 :                 if (type_correct == false) {
      71           0 :                         return -1;
      72             :                 }
      73         117 :                 if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
      74           0 :                         PyErr_NoMemory();
      75           0 :                         return -1;
      76             :                 }
      77         117 :                 recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
      78             :         }
      79         842 :         *records = recs;
      80         842 :         *num_records = PyList_GET_SIZE(value);
      81         842 :         return 0;
      82             : }
      83             : 
      84         450 : static PyObject *py_dsdb_dns_lookup(PyObject *self,
      85             :                                     PyObject *args, PyObject *kwargs)
      86             : {
      87           6 :         struct ldb_context *samdb;
      88           6 :         PyObject *py_ldb, *ret, *pydn;
      89         450 :         PyObject *py_dns_partition = NULL;
      90         450 :         PyObject *result = NULL;
      91           6 :         char *dns_name;
      92           6 :         TALLOC_CTX *frame;
      93           6 :         NTSTATUS status;
      94           6 :         WERROR werr;
      95           6 :         struct dns_server_zone *zones_list;
      96         450 :         struct ldb_dn *dn, *dns_partition = NULL;
      97           6 :         struct dnsp_DnssrvRpcRecord *records;
      98           6 :         uint16_t num_records;
      99         450 :         const char * const kwnames[] = { "ldb", "dns_name",
     100             :                                          "dns_partition", NULL };
     101             : 
     102         450 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
     103             :                                          discard_const_p(char *, kwnames),
     104             :                                          &py_ldb, &dns_name,
     105             :                                          &py_dns_partition)) {
     106           0 :                 return NULL;
     107             :         }
     108         450 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     109             : 
     110         450 :         if (py_dns_partition) {
     111          93 :                 PyErr_LDB_DN_OR_RAISE(py_dns_partition,
     112             :                                       dns_partition);
     113             :         }
     114             : 
     115         450 :         frame = talloc_stackframe();
     116             : 
     117         450 :         status = dns_common_zones(samdb, frame, dns_partition,
     118             :                                   &zones_list);
     119         450 :         if (!NT_STATUS_IS_OK(status)) {
     120           0 :                 talloc_free(frame);
     121           0 :                 PyErr_SetNTSTATUS(status);
     122           0 :                 return NULL;
     123             :         }
     124             : 
     125         450 :         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
     126         450 :         if (!W_ERROR_IS_OK(werr)) {
     127          12 :                 talloc_free(frame);
     128          12 :                 PyErr_SetWERROR(werr);
     129          12 :                 return NULL;
     130             :         }
     131             : 
     132         438 :         werr = dns_common_lookup(samdb,
     133             :                                  frame,
     134             :                                  dn,
     135             :                                  &records,
     136             :                                  &num_records,
     137             :                                  NULL);
     138         438 :         if (!W_ERROR_IS_OK(werr)) {
     139           8 :                 talloc_free(frame);
     140           8 :                 PyErr_SetWERROR(werr);
     141           8 :                 return NULL;
     142             :         }
     143             : 
     144         430 :         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
     145         430 :         pydn = pyldb_Dn_FromDn(dn, (PyLdbObject *)py_ldb);
     146         430 :         talloc_free(frame);
     147         430 :         result = Py_BuildValue("(OO)", pydn, ret);
     148         430 :         Py_CLEAR(ret);
     149         430 :         Py_CLEAR(pydn);
     150         426 :         return result;
     151             : }
     152             : 
     153           0 : static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
     154             : {
     155           0 :         struct ldb_context *samdb;
     156           0 :         PyObject *py_dns_el, *ret;
     157           0 :         PyObject *py_ldb = NULL;
     158           0 :         TALLOC_CTX *frame;
     159           0 :         WERROR werr;
     160           0 :         struct ldb_message_element *dns_el;
     161           0 :         struct dnsp_DnssrvRpcRecord *records;
     162           0 :         uint16_t num_records;
     163             : 
     164           0 :         if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
     165           0 :                 return NULL;
     166             :         }
     167             : 
     168           0 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     169             : 
     170           0 :         if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
     171           0 :                 PyErr_SetString(PyExc_TypeError,
     172             :                                 "ldb MessageElement object required");
     173           0 :                 return NULL;
     174             :         }
     175           0 :         dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
     176             : 
     177           0 :         frame = talloc_stackframe();
     178             : 
     179           0 :         werr = dns_common_extract(samdb, dns_el,
     180             :                                   frame,
     181             :                                   &records,
     182             :                                   &num_records);
     183           0 :         if (!W_ERROR_IS_OK(werr)) {
     184           0 :                 talloc_free(frame);
     185           0 :                 PyErr_SetWERROR(werr);
     186           0 :                 return NULL;
     187             :         }
     188             : 
     189           0 :         ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
     190           0 :         talloc_free(frame);
     191           0 :         return ret;
     192             : }
     193             : 
     194         166 : static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
     195             : {
     196           4 :         struct ldb_context *samdb;
     197           4 :         PyObject *py_ldb, *py_dns_records;
     198           4 :         char *dns_name;
     199           4 :         TALLOC_CTX *frame;
     200           4 :         NTSTATUS status;
     201           4 :         WERROR werr;
     202           4 :         int ret;
     203           4 :         struct dns_server_zone *zones_list;
     204           4 :         struct ldb_dn *dn;
     205           4 :         struct dnsp_DnssrvRpcRecord *records;
     206           4 :         uint16_t num_records;
     207             : 
     208             :         /*
     209             :          * TODO: This is a shocking abuse, but matches what the
     210             :          * internal DNS server does, it should be pushed into
     211             :          * dns_common_replace()
     212             :          */
     213           4 :         static const int serial = 110;
     214             : 
     215         166 :         if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
     216           0 :                 return NULL;
     217             :         }
     218         166 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     219             : 
     220         166 :         frame = talloc_stackframe();
     221             : 
     222         166 :         status = dns_common_zones(samdb, frame, NULL, &zones_list);
     223         166 :         if (!NT_STATUS_IS_OK(status)) {
     224           0 :                 PyErr_SetNTSTATUS(status);
     225           0 :                 talloc_free(frame);
     226           0 :                 return NULL;
     227             :         }
     228             : 
     229         166 :         werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
     230         166 :         if (!W_ERROR_IS_OK(werr)) {
     231           0 :                 PyErr_SetWERROR(werr);
     232           0 :                 talloc_free(frame);
     233           0 :                 return NULL;
     234             :         }
     235             : 
     236         166 :         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
     237             :                                                 frame,
     238             :                                                 &records, &num_records);
     239         166 :         if (ret != 0) {
     240           0 :                 talloc_free(frame);
     241           0 :                 return NULL;
     242             :         }
     243             : 
     244         166 :         werr = dns_common_replace(samdb,
     245             :                                   frame,
     246             :                                   dn,
     247             :                                   false, /* Not adding a record */
     248             :                                   serial,
     249             :                                   records,
     250             :                                   num_records);
     251         166 :         if (!W_ERROR_IS_OK(werr)) {
     252           0 :                 PyErr_SetWERROR(werr);
     253           0 :                 talloc_free(frame);
     254           0 :                 return NULL;
     255             :         }
     256             : 
     257         166 :         talloc_free(frame);
     258         166 :         Py_RETURN_NONE;
     259             : }
     260             : 
     261         676 : static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
     262             : {
     263          25 :         struct ldb_context *samdb;
     264          25 :         PyObject *py_ldb, *py_dn, *py_dns_records;
     265          25 :         TALLOC_CTX *frame;
     266          25 :         WERROR werr;
     267          25 :         int ret;
     268          25 :         struct ldb_dn *dn;
     269          25 :         struct dnsp_DnssrvRpcRecord *records;
     270          25 :         uint16_t num_records;
     271             : 
     272             :         /*
     273             :          * TODO: This is a shocking abuse, but matches what the
     274             :          * internal DNS server does, it should be pushed into
     275             :          * dns_common_replace()
     276             :          */
     277          25 :         static const int serial = 110;
     278             : 
     279         676 :         if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
     280           0 :                 return NULL;
     281             :         }
     282         676 :         PyErr_LDB_OR_RAISE(py_ldb, samdb);
     283             : 
     284         676 :         PyErr_LDB_DN_OR_RAISE(py_dn, dn);
     285             : 
     286         676 :         frame = talloc_stackframe();
     287             : 
     288         676 :         ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
     289             :                                                 frame,
     290             :                                                 &records, &num_records);
     291         676 :         if (ret != 0) {
     292           0 :                 talloc_free(frame);
     293           0 :                 return NULL;
     294             :         }
     295             : 
     296         676 :         werr = dns_common_replace(samdb,
     297             :                                   frame,
     298             :                                   dn,
     299             :                                   false, /* Not adding a node */
     300             :                                   serial,
     301             :                                   records,
     302             :                                   num_records);
     303         676 :         if (!W_ERROR_IS_OK(werr)) {
     304           0 :                 PyErr_SetWERROR(werr);
     305           0 :                 talloc_free(frame);
     306           0 :                 return NULL;
     307             :         }
     308             : 
     309         676 :         talloc_free(frame);
     310             : 
     311         676 :         Py_RETURN_NONE;
     312             : }
     313             : 
     314             : 
     315        1458 : static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
     316             : {
     317           0 :         PyObject *py_recs[2];
     318           0 :         struct dnsp_DnssrvRpcRecord *rec1;
     319           0 :         struct dnsp_DnssrvRpcRecord *rec2;
     320           0 :         size_t i;
     321           0 :         bool type_correct;
     322           0 :         bool match;
     323             : 
     324        1458 :         if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
     325           0 :                 return NULL;
     326             :         }
     327             : 
     328        4374 :         for (i = 0; i < 2; i++) {
     329        2916 :                 type_correct = py_check_dcerpc_type(py_recs[i],
     330             :                                                     "samba.dcerpc.dnsp",
     331             :                                                     "DnssrvRpcRecord");
     332        2916 :                 if (! type_correct) {
     333           0 :                         PyErr_SetString(PyExc_ValueError,
     334             :                                         "DnssrvRpcRecord expected");
     335           0 :                         return NULL;
     336             :                 }
     337             :         }
     338             : 
     339        1458 :         rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
     340        1458 :         rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
     341             : 
     342        1458 :         match = dns_record_match(rec1, rec2);
     343        1458 :         return PyBool_FromLong(match);
     344             : }
     345             : 
     346             : 
     347         207 : static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
     348             : {
     349           0 :         uint32_t timestamp;
     350           0 :         time_t t;
     351           0 :         long long lt;
     352             : 
     353         207 :         if (!PyArg_ParseTuple(args, "L", &lt)) {
     354           0 :                 return NULL;
     355             :         }
     356             : 
     357         207 :         t = lt;
     358         207 :         if (t != lt) {
     359             :                 /* time_t is presumably 32 bit here */
     360           0 :                 PyErr_SetString(PyExc_ValueError, "Time out of range");
     361           0 :                 return NULL;
     362             :         }
     363         207 :         timestamp = unix_to_dns_timestamp(t);
     364         207 :         return Py_BuildValue("k", (unsigned long) timestamp);
     365             : }
     366             : 
     367          11 : static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
     368             : {
     369           0 :         unsigned long long timestamp;
     370           0 :         NTSTATUS status;
     371           0 :         NTTIME nt;
     372          11 :         if (!PyArg_ParseTuple(args, "K", &timestamp)) {
     373           0 :                 return NULL;
     374             :         }
     375             : 
     376          11 :         if (timestamp > UINT32_MAX) {
     377           1 :                 PyErr_SetString(PyExc_ValueError, "Time out of range");
     378           1 :                 return NULL;
     379             :         }
     380          10 :         status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
     381          10 :         if (!NT_STATUS_IS_OK(status)) {
     382           2 :                 PyErr_SetString(PyExc_ValueError, "Time out of range");
     383           2 :                 return NULL;
     384             :         }
     385           8 :         return Py_BuildValue("L", (long long) nt);
     386             : }
     387             : 
     388             : 
     389             : static PyMethodDef py_dsdb_dns_methods[] = {
     390             : 
     391             :         { "lookup", PY_DISCARD_FUNC_SIG(PyCFunction, py_dsdb_dns_lookup),
     392             :                 METH_VARARGS|METH_KEYWORDS,
     393             :                 "Get the DNS database entries for a DNS name"},
     394             :         { "replace", (PyCFunction)py_dsdb_dns_replace,
     395             :                 METH_VARARGS, "Replace the DNS database entries for a DNS name"},
     396             :         { "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
     397             :                 METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
     398             :         { "records_match", (PyCFunction)py_dsdb_dns_records_match,
     399             :           METH_VARARGS|METH_KEYWORDS,
     400             :           "Decide whether two records match, according to dns update rules"},
     401             :         { "extract", (PyCFunction)py_dsdb_dns_extract,
     402             :                 METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
     403             :         { "unix_to_dns_timestamp", (PyCFunction)py_dsdb_dns_unix_to_dns_timestamp,
     404             :           METH_VARARGS,
     405             :           "Convert a time.time() value to a dns timestamp (hours since 1601)"},
     406             :         { "dns_timestamp_to_nt_time", (PyCFunction)py_dsdb_dns_timestamp_to_nt_time,
     407             :           METH_VARARGS,
     408             :           "Convert a dns timestamp to an NTTIME value"},
     409             :         {0}
     410             : };
     411             : 
     412             : static struct PyModuleDef moduledef = {
     413             :     PyModuleDef_HEAD_INIT,
     414             :     .m_name = "dsdb_dns",
     415             :     .m_doc = "Python bindings for the DNS objects in the directory service databases.",
     416             :     .m_size = -1,
     417             :     .m_methods = py_dsdb_dns_methods,
     418             : };
     419             : 
     420        7630 : MODULE_INIT_FUNC(dsdb_dns)
     421             : {
     422         192 :         PyObject *m;
     423             : 
     424        7630 :         m = PyModule_Create(&moduledef);
     425             : 
     426        7630 :         if (m == NULL)
     427           0 :                 return NULL;
     428             : 
     429        7438 :         return m;
     430             : }

Generated by: LCOV version 1.14