/*
Unix SMB/CIFS implementation.
Python DNS server wrapper
Copyright (C) 2015 Andrew Bartlett
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "lib/replace/system/python.h"
#include "python/py3compat.h"
#include "includes.h"
#include "python/modules.h"
#include
#include
#include "dns_server/dnsserver_common.h"
#include "dsdb/samdb/samdb.h"
#include "dsdb/common/util.h"
#include "librpc/gen_ndr/ndr_dnsp.h"
#include "librpc/rpc/pyrpc_util.h"
/* FIXME: These should be in a header file somewhere */
#define PyErr_LDB_OR_RAISE(py_ldb, ldb) \
if (!py_check_dcerpc_type(py_ldb, "ldb", "Ldb")) { \
PyErr_SetString(PyExc_TypeError, "Ldb connection object required"); \
return NULL; \
} \
ldb = pyldb_Ldb_AsLdbContext(py_ldb);
#define PyErr_LDB_DN_OR_RAISE(py_ldb_dn, dn) \
if (!py_check_dcerpc_type(py_ldb_dn, "ldb", "Dn")) { \
PyErr_SetString(PyExc_TypeError, "ldb Dn object required"); \
return NULL; \
} \
dn = pyldb_Dn_AS_DN(py_ldb_dn);
static PyObject *py_dnsp_DnssrvRpcRecord_get_list(struct dnsp_DnssrvRpcRecord *records,
uint16_t num_records)
{
PyObject *py_dns_list;
int i;
py_dns_list = PyList_New(num_records);
if (py_dns_list == NULL) {
return NULL;
}
for (i = 0; i < num_records; i++) {
PyObject *py_dns_record;
py_dns_record = py_return_ndr_struct("samba.dcerpc.dnsp", "DnssrvRpcRecord", records, &records[i]);
PyList_SetItem(py_dns_list, i, py_dns_record);
}
return py_dns_list;
}
static int py_dnsp_DnssrvRpcRecord_get_array(PyObject *value,
TALLOC_CTX *mem_ctx,
struct dnsp_DnssrvRpcRecord **records,
uint16_t *num_records)
{
int i;
struct dnsp_DnssrvRpcRecord *recs;
PY_CHECK_TYPE(&PyList_Type, value, return -1;);
recs = talloc_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
PyList_GET_SIZE(value));
if (recs == NULL) {
PyErr_NoMemory();
return -1;
}
for (i = 0; i < PyList_GET_SIZE(value); i++) {
bool type_correct;
PyObject *item = PyList_GET_ITEM(value, i);
type_correct = py_check_dcerpc_type(item, "samba.dcerpc.dnsp", "DnssrvRpcRecord");
if (type_correct == false) {
return -1;
}
if (talloc_reference(mem_ctx, pytalloc_get_mem_ctx(item)) == NULL) {
PyErr_NoMemory();
return -1;
}
recs[i] = *(struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(item);
}
*records = recs;
*num_records = PyList_GET_SIZE(value);
return 0;
}
static PyObject *py_dsdb_dns_lookup(PyObject *self,
PyObject *args, PyObject *kwargs)
{
struct ldb_context *samdb;
PyObject *py_ldb, *ret, *pydn;
PyObject *py_dns_partition = NULL;
PyObject *result = NULL;
char *dns_name;
TALLOC_CTX *frame;
NTSTATUS status;
WERROR werr;
struct dns_server_zone *zones_list;
struct ldb_dn *dn, *dns_partition = NULL;
struct dnsp_DnssrvRpcRecord *records;
uint16_t num_records;
const char * const kwnames[] = { "ldb", "dns_name",
"dns_partition", NULL };
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|O",
discard_const_p(char *, kwnames),
&py_ldb, &dns_name,
&py_dns_partition)) {
return NULL;
}
PyErr_LDB_OR_RAISE(py_ldb, samdb);
if (py_dns_partition) {
PyErr_LDB_DN_OR_RAISE(py_dns_partition,
dns_partition);
}
frame = talloc_stackframe();
status = dns_common_zones(samdb, frame, dns_partition,
&zones_list);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(frame);
PyErr_SetNTSTATUS(status);
return NULL;
}
werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
if (!W_ERROR_IS_OK(werr)) {
talloc_free(frame);
PyErr_SetWERROR(werr);
return NULL;
}
werr = dns_common_lookup(samdb,
frame,
dn,
&records,
&num_records,
NULL);
if (!W_ERROR_IS_OK(werr)) {
talloc_free(frame);
PyErr_SetWERROR(werr);
return NULL;
}
ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
pydn = pyldb_Dn_FromDn(dn);
talloc_free(frame);
result = Py_BuildValue("(OO)", pydn, ret);
Py_CLEAR(ret);
Py_CLEAR(pydn);
return result;
}
static PyObject *py_dsdb_dns_extract(PyObject *self, PyObject *args)
{
struct ldb_context *samdb;
PyObject *py_dns_el, *ret;
PyObject *py_ldb = NULL;
TALLOC_CTX *frame;
WERROR werr;
struct ldb_message_element *dns_el;
struct dnsp_DnssrvRpcRecord *records;
uint16_t num_records;
if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_dns_el)) {
return NULL;
}
PyErr_LDB_OR_RAISE(py_ldb, samdb);
if (!py_check_dcerpc_type(py_dns_el, "ldb", "MessageElement")) {
PyErr_SetString(PyExc_TypeError,
"ldb MessageElement object required");
return NULL;
}
dns_el = pyldb_MessageElement_AsMessageElement(py_dns_el);
frame = talloc_stackframe();
werr = dns_common_extract(samdb, dns_el,
frame,
&records,
&num_records);
if (!W_ERROR_IS_OK(werr)) {
talloc_free(frame);
PyErr_SetWERROR(werr);
return NULL;
}
ret = py_dnsp_DnssrvRpcRecord_get_list(records, num_records);
talloc_free(frame);
return ret;
}
static PyObject *py_dsdb_dns_replace(PyObject *self, PyObject *args)
{
struct ldb_context *samdb;
PyObject *py_ldb, *py_dns_records;
char *dns_name;
TALLOC_CTX *frame;
NTSTATUS status;
WERROR werr;
int ret;
struct dns_server_zone *zones_list;
struct ldb_dn *dn;
struct dnsp_DnssrvRpcRecord *records;
uint16_t num_records;
/*
* TODO: This is a shocking abuse, but matches what the
* internal DNS server does, it should be pushed into
* dns_common_replace()
*/
static const int serial = 110;
if (!PyArg_ParseTuple(args, "OsO", &py_ldb, &dns_name, &py_dns_records)) {
return NULL;
}
PyErr_LDB_OR_RAISE(py_ldb, samdb);
frame = talloc_stackframe();
status = dns_common_zones(samdb, frame, NULL, &zones_list);
if (!NT_STATUS_IS_OK(status)) {
PyErr_SetNTSTATUS(status);
talloc_free(frame);
return NULL;
}
werr = dns_common_name2dn(samdb, zones_list, frame, dns_name, &dn);
if (!W_ERROR_IS_OK(werr)) {
PyErr_SetWERROR(werr);
talloc_free(frame);
return NULL;
}
ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
frame,
&records, &num_records);
if (ret != 0) {
talloc_free(frame);
return NULL;
}
werr = dns_common_replace(samdb,
frame,
dn,
false, /* Not adding a record */
serial,
records,
num_records);
if (!W_ERROR_IS_OK(werr)) {
PyErr_SetWERROR(werr);
talloc_free(frame);
return NULL;
}
talloc_free(frame);
Py_RETURN_NONE;
}
static PyObject *py_dsdb_dns_replace_by_dn(PyObject *self, PyObject *args)
{
struct ldb_context *samdb;
PyObject *py_ldb, *py_dn, *py_dns_records;
TALLOC_CTX *frame;
WERROR werr;
int ret;
struct ldb_dn *dn;
struct dnsp_DnssrvRpcRecord *records;
uint16_t num_records;
/*
* TODO: This is a shocking abuse, but matches what the
* internal DNS server does, it should be pushed into
* dns_common_replace()
*/
static const int serial = 110;
if (!PyArg_ParseTuple(args, "OOO", &py_ldb, &py_dn, &py_dns_records)) {
return NULL;
}
PyErr_LDB_OR_RAISE(py_ldb, samdb);
PyErr_LDB_DN_OR_RAISE(py_dn, dn);
frame = talloc_stackframe();
ret = py_dnsp_DnssrvRpcRecord_get_array(py_dns_records,
frame,
&records, &num_records);
if (ret != 0) {
talloc_free(frame);
return NULL;
}
werr = dns_common_replace(samdb,
frame,
dn,
false, /* Not adding a node */
serial,
records,
num_records);
if (!W_ERROR_IS_OK(werr)) {
PyErr_SetWERROR(werr);
talloc_free(frame);
return NULL;
}
talloc_free(frame);
Py_RETURN_NONE;
}
static PyObject *py_dsdb_dns_records_match(PyObject *self, PyObject *args)
{
PyObject *py_recs[2];
struct dnsp_DnssrvRpcRecord *rec1;
struct dnsp_DnssrvRpcRecord *rec2;
size_t i;
bool type_correct;
bool match;
if (!PyArg_ParseTuple(args, "OO", &py_recs[0], &py_recs[1])) {
return NULL;
}
for (i = 0; i < 2; i++) {
type_correct = py_check_dcerpc_type(py_recs[i],
"samba.dcerpc.dnsp",
"DnssrvRpcRecord");
if (! type_correct) {
PyErr_SetString(PyExc_ValueError,
"DnssrvRpcRecord expected");
return NULL;
}
}
rec1 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[0]);
rec2 = (struct dnsp_DnssrvRpcRecord *)pytalloc_get_ptr(py_recs[1]);
match = dns_record_match(rec1, rec2);
return PyBool_FromLong(match);
}
static PyObject *py_dsdb_dns_unix_to_dns_timestamp(PyObject *self, PyObject *args)
{
uint32_t timestamp;
time_t t;
long long lt;
if (!PyArg_ParseTuple(args, "L", <)) {
return NULL;
}
t = lt;
if (t != lt) {
/* time_t is presumably 32 bit here */
PyErr_SetString(PyExc_ValueError, "Time out of range");
return NULL;
}
timestamp = unix_to_dns_timestamp(t);
return Py_BuildValue("k", (unsigned long) timestamp);
}
static PyObject *py_dsdb_dns_timestamp_to_nt_time(PyObject *self, PyObject *args)
{
unsigned long long timestamp;
NTSTATUS status;
NTTIME nt;
if (!PyArg_ParseTuple(args, "K", ×tamp)) {
return NULL;
}
if (timestamp > UINT32_MAX) {
PyErr_SetString(PyExc_ValueError, "Time out of range");
return NULL;
}
status = dns_timestamp_to_nt_time(&nt, (uint32_t)timestamp);
if (!NT_STATUS_IS_OK(status)) {
PyErr_SetString(PyExc_ValueError, "Time out of range");
return NULL;
}
return Py_BuildValue("L", (long long) nt);
}
static PyMethodDef py_dsdb_dns_methods[] = {
{ "lookup", PY_DISCARD_FUNC_SIG(PyCFunction, py_dsdb_dns_lookup),
METH_VARARGS|METH_KEYWORDS,
"Get the DNS database entries for a DNS name"},
{ "replace", (PyCFunction)py_dsdb_dns_replace,
METH_VARARGS, "Replace the DNS database entries for a DNS name"},
{ "replace_by_dn", (PyCFunction)py_dsdb_dns_replace_by_dn,
METH_VARARGS, "Replace the DNS database entries for a LDB DN"},
{ "records_match", (PyCFunction)py_dsdb_dns_records_match,
METH_VARARGS|METH_KEYWORDS,
"Decide whether two records match, according to dns update rules"},
{ "extract", (PyCFunction)py_dsdb_dns_extract,
METH_VARARGS, "Return the DNS database entry as a python structure from an Ldb.MessageElement of type dnsRecord"},
{ "unix_to_dns_timestamp", (PyCFunction)py_dsdb_dns_unix_to_dns_timestamp,
METH_VARARGS,
"Convert a time.time() value to a dns timestamp (hours since 1601)"},
{ "dns_timestamp_to_nt_time", (PyCFunction)py_dsdb_dns_timestamp_to_nt_time,
METH_VARARGS,
"Convert a dns timestamp to an NTTIME value"},
{0}
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
.m_name = "dsdb_dns",
.m_doc = "Python bindings for the DNS objects in the directory service databases.",
.m_size = -1,
.m_methods = py_dsdb_dns_methods,
};
MODULE_INIT_FUNC(dsdb_dns)
{
PyObject *m;
m = PyModule_Create(&moduledef);
if (m == NULL)
return NULL;
return m;
}