1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source4/dns_server/dns_server.c
Stefan Metzmacher 40fb810de3 s4:dns_server: Add some more debugging in order to find problems with level 10 logs
We had customer problems where level 10 logs were not good enough in
order to find the reason for failing dns updates.

With the new debug message there's at least a chance to
find out what the problem could be.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
2023-07-19 09:58:37 +00:00

964 lines
26 KiB
C

/*
Unix SMB/CIFS implementation.
DNS server startup
Copyright (C) 2010 Kai Blin <kai@samba.org>
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 <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "samba/service_task.h"
#include "samba/service.h"
#include "samba/service_stream.h"
#include "samba/process_model.h"
#include "lib/events/events.h"
#include "lib/socket/socket.h"
#include "lib/tsocket/tsocket.h"
#include "libcli/util/tstream.h"
#include "libcli/util/ntstatus.h"
#include "system/network.h"
#include "lib/stream/packet.h"
#include "lib/socket/netif.h"
#include "dns_server/dns_server.h"
#include "param/param.h"
#include "librpc/ndr/libndr.h"
#include "librpc/gen_ndr/ndr_dns.h"
#include "librpc/gen_ndr/ndr_dnsp.h"
#include <ldb.h>
#include "dsdb/samdb/samdb.h"
#include "dsdb/common/util.h"
#include "auth/session.h"
#include "lib/util/dlinklist.h"
#include "lib/util/tevent_werror.h"
#include "auth/auth.h"
#include "auth/credentials/credentials.h"
#include "librpc/gen_ndr/ndr_irpc.h"
#include "lib/messaging/irpc.h"
#include "libds/common/roles.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_DNS
NTSTATUS server_service_dns_init(TALLOC_CTX *);
/* hold information about one dns socket */
struct dns_socket {
struct dns_server *dns;
struct tsocket_address *local_address;
};
struct dns_udp_socket {
struct dns_socket *dns_socket;
struct tdgram_context *dgram;
struct tevent_queue *send_queue;
};
/*
state of an open tcp connection
*/
struct dns_tcp_connection {
/* stream connection we belong to */
struct stream_connection *conn;
/* the dns_server the connection belongs to */
struct dns_socket *dns_socket;
struct tstream_context *tstream;
struct tevent_queue *send_queue;
};
static void dns_tcp_terminate_connection(struct dns_tcp_connection *dnsconn, const char *reason)
{
stream_terminate_connection(dnsconn->conn, reason);
}
static void dns_tcp_recv(struct stream_connection *conn, uint16_t flags)
{
struct dns_tcp_connection *dnsconn = talloc_get_type(conn->private_data,
struct dns_tcp_connection);
/* this should never be triggered! */
dns_tcp_terminate_connection(dnsconn, "dns_tcp_recv: called");
}
static void dns_tcp_send(struct stream_connection *conn, uint16_t flags)
{
struct dns_tcp_connection *dnsconn = talloc_get_type(conn->private_data,
struct dns_tcp_connection);
/* this should never be triggered! */
dns_tcp_terminate_connection(dnsconn, "dns_tcp_send: called");
}
struct dns_process_state {
DATA_BLOB *in;
struct dns_server *dns;
struct dns_name_packet in_packet;
struct dns_request_state state;
WERROR dns_err;
struct dns_name_packet out_packet;
DATA_BLOB out;
};
static void dns_process_done(struct tevent_req *subreq);
static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct dns_server *dns,
const struct tsocket_address *remote_address,
const struct tsocket_address *local_address,
DATA_BLOB *in)
{
struct tevent_req *req, *subreq;
struct dns_process_state *state;
enum ndr_err_code ndr_err;
WERROR ret;
const char **forwarder = lpcfg_dns_forwarder(dns->task->lp_ctx);
req = tevent_req_create(mem_ctx, &state, struct dns_process_state);
if (req == NULL) {
return NULL;
}
state->state.mem_ctx = state;
state->in = in;
state->dns = dns;
if (in->length < 12) {
tevent_req_werror(req, WERR_INVALID_PARAMETER);
return tevent_req_post(req, ev);
}
dump_data_dbgc(DBGC_DNS, 8, in->data, in->length);
ndr_err = ndr_pull_struct_blob(
in, state, &state->in_packet,
(ndr_pull_flags_fn_t)ndr_pull_dns_name_packet);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
DBG_NOTICE("ndr_pull_dns_name_packet() failed with %s\n",
ndr_map_error2string(ndr_err));
state->dns_err = DNS_ERR(FORMAT_ERROR);
tevent_req_done(req);
return tevent_req_post(req, ev);
}
if (DEBUGLVLC(DBGC_DNS, 8)) {
NDR_PRINT_DEBUGC(DBGC_DNS, dns_name_packet, &state->in_packet);
}
if (state->in_packet.operation & DNS_FLAG_REPLY) {
DBG_INFO("Won't reply to replies.\n");
tevent_req_werror(req, WERR_INVALID_PARAMETER);
return tevent_req_post(req, ev);
}
state->state.flags = state->in_packet.operation;
state->state.flags |= DNS_FLAG_REPLY;
state->state.local_address = local_address;
state->state.remote_address = remote_address;
if (forwarder && *forwarder && **forwarder) {
state->state.flags |= DNS_FLAG_RECURSION_AVAIL;
}
state->out_packet = state->in_packet;
ret = dns_verify_tsig(dns, state, &state->state,
&state->out_packet, in);
if (!W_ERROR_IS_OK(ret)) {
DBG_INFO("dns_verify_tsig() failed with %s\n",
win_errstr(ret));
state->dns_err = ret;
tevent_req_done(req);
return tevent_req_post(req, ev);
}
switch (state->in_packet.operation & DNS_OPCODE) {
case DNS_OPCODE_QUERY:
subreq = dns_server_process_query_send(
state, ev, dns,
&state->state, &state->in_packet);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, dns_process_done, req);
return req;
case DNS_OPCODE_UPDATE:
ret = dns_server_process_update(
dns, &state->state, state, &state->in_packet,
&state->out_packet.answers, &state->out_packet.ancount,
&state->out_packet.nsrecs, &state->out_packet.nscount,
&state->out_packet.additional,
&state->out_packet.arcount);
DBG_DEBUG("dns_server_process_update(): %s\n",
win_errstr(ret));
break;
default:
ret = WERR_DNS_ERROR_RCODE_NOT_IMPLEMENTED;
DBG_NOTICE("OPCODE[0x%x]: %s\n",
(state->in_packet.operation & DNS_OPCODE),
win_errstr(ret));
}
state->dns_err = ret;
tevent_req_done(req);
return tevent_req_post(req, ev);
}
static void dns_process_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct dns_process_state *state = tevent_req_data(
req, struct dns_process_state);
WERROR ret;
ret = dns_server_process_query_recv(
subreq, state,
&state->out_packet.answers, &state->out_packet.ancount,
&state->out_packet.nsrecs, &state->out_packet.nscount,
&state->out_packet.additional, &state->out_packet.arcount);
TALLOC_FREE(subreq);
DBG_DEBUG("dns_server_process_query_recv(): %s\n",
win_errstr(ret));
state->dns_err = ret;
tevent_req_done(req);
}
static WERROR dns_process_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
DATA_BLOB *out)
{
struct dns_process_state *state = tevent_req_data(
req, struct dns_process_state);
enum ndr_err_code ndr_err;
uint16_t dns_err;
WERROR ret;
if (tevent_req_is_werror(req, &ret)) {
DBG_NOTICE("ERROR: %s from %s\n", win_errstr(ret),
tevent_req_print(state, req));
return ret;
}
dns_err = werr_to_dns_err(state->dns_err);
if ((dns_err != DNS_RCODE_OK) &&
(dns_err != DNS_RCODE_NXDOMAIN) &&
(dns_err != DNS_RCODE_NOTAUTH))
{
DBG_INFO("FAILURE: %s from %s\n",
win_errstr(state->dns_err),
tevent_req_print(state, req));
goto drop;
}
if (dns_err != DNS_RCODE_OK) {
DBG_DEBUG("INFO: %s from %s\n",
win_errstr(state->dns_err),
tevent_req_print(state, req));
state->out_packet.operation |= dns_err;
} else {
DBG_DEBUG("OK: %s\n",
tevent_req_print(state, req));
}
state->out_packet.operation |= state->state.flags;
if (state->state.sign) {
ret = dns_sign_tsig(state->dns, mem_ctx, &state->state,
&state->out_packet, 0);
if (!W_ERROR_IS_OK(ret)) {
DBG_WARNING("dns_sign_tsig() failed %s\n",
win_errstr(ret));
dns_err = DNS_RCODE_SERVFAIL;
goto drop;
}
}
if (DEBUGLVLC(DBGC_DNS, 8)) {
NDR_PRINT_DEBUGC(DBGC_DNS, dns_name_packet, &state->out_packet);
}
ndr_err = ndr_push_struct_blob(
out, mem_ctx, &state->out_packet,
(ndr_push_flags_fn_t)ndr_push_dns_name_packet);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
DBG_WARNING("Failed to push packet: %s!\n",
ndr_errstr(ndr_err));
dns_err = DNS_RCODE_SERVFAIL;
goto drop;
}
return WERR_OK;
drop:
*out = data_blob_talloc(mem_ctx, state->in->data, state->in->length);
if (out->data == NULL) {
return WERR_NOT_ENOUGH_MEMORY;
}
out->data[2] |= 0x80; /* Toggle DNS_FLAG_REPLY */
out->data[3] |= dns_err;
return WERR_OK;
}
struct dns_tcp_call {
struct dns_tcp_connection *dns_conn;
DATA_BLOB in;
DATA_BLOB out;
uint8_t out_hdr[4];
struct iovec out_iov[2];
};
static void dns_tcp_call_process_done(struct tevent_req *subreq);
static void dns_tcp_call_writev_done(struct tevent_req *subreq);
static void dns_tcp_call_loop(struct tevent_req *subreq)
{
struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
struct dns_tcp_connection);
struct dns_server *dns = dns_conn->dns_socket->dns;
struct dns_tcp_call *call;
NTSTATUS status;
call = talloc(dns_conn, struct dns_tcp_call);
if (call == NULL) {
dns_tcp_terminate_connection(dns_conn, "dns_tcp_call_loop: "
"no memory for dns_tcp_call");
return;
}
call->dns_conn = dns_conn;
status = tstream_read_pdu_blob_recv(subreq,
call,
&call->in);
TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) {
const char *reason;
reason = talloc_asprintf(call, "dns_tcp_call_loop: "
"tstream_read_pdu_blob_recv() - %s",
nt_errstr(status));
if (!reason) {
reason = nt_errstr(status);
}
dns_tcp_terminate_connection(dns_conn, reason);
return;
}
DEBUG(10,("Received DNS TCP packet of length %lu from %s\n",
(long) call->in.length,
tsocket_address_string(dns_conn->conn->remote_address, call)));
/* skip length header */
call->in.data += 2;
call->in.length -= 2;
subreq = dns_process_send(call, dns->task->event_ctx, dns,
dns_conn->conn->remote_address,
dns_conn->conn->local_address,
&call->in);
if (subreq == NULL) {
dns_tcp_terminate_connection(
dns_conn, "dns_tcp_call_loop: dns_process_send "
"failed\n");
return;
}
tevent_req_set_callback(subreq, dns_tcp_call_process_done, call);
/*
* The dns tcp pdu's has the length as 2 byte (initial_read_size),
* packet_full_request_u16 provides the pdu length then.
*/
subreq = tstream_read_pdu_blob_send(dns_conn,
dns_conn->conn->event.ctx,
dns_conn->tstream,
2, /* initial_read_size */
packet_full_request_u16,
dns_conn);
if (subreq == NULL) {
dns_tcp_terminate_connection(dns_conn, "dns_tcp_call_loop: "
"no memory for tstream_read_pdu_blob_send");
return;
}
tevent_req_set_callback(subreq, dns_tcp_call_loop, dns_conn);
}
static void dns_tcp_call_process_done(struct tevent_req *subreq)
{
struct dns_tcp_call *call = tevent_req_callback_data(subreq,
struct dns_tcp_call);
struct dns_tcp_connection *dns_conn = call->dns_conn;
WERROR err;
err = dns_process_recv(subreq, call, &call->out);
TALLOC_FREE(subreq);
if (!W_ERROR_IS_OK(err)) {
DEBUG(1, ("dns_process returned %s\n", win_errstr(err)));
dns_tcp_terminate_connection(dns_conn,
"dns_tcp_call_loop: process function failed");
return;
}
/* First add the length of the out buffer */
RSSVAL(call->out_hdr, 0, call->out.length);
call->out_iov[0].iov_base = (char *) call->out_hdr;
call->out_iov[0].iov_len = 2;
call->out_iov[1].iov_base = (char *) call->out.data;
call->out_iov[1].iov_len = call->out.length;
subreq = tstream_writev_queue_send(call,
dns_conn->conn->event.ctx,
dns_conn->tstream,
dns_conn->send_queue,
call->out_iov, 2);
if (subreq == NULL) {
dns_tcp_terminate_connection(dns_conn, "dns_tcp_call_loop: "
"no memory for tstream_writev_queue_send");
return;
}
tevent_req_set_callback(subreq, dns_tcp_call_writev_done, call);
}
static void dns_tcp_call_writev_done(struct tevent_req *subreq)
{
struct dns_tcp_call *call = tevent_req_callback_data(subreq,
struct dns_tcp_call);
int sys_errno;
int rc;
rc = tstream_writev_queue_recv(subreq, &sys_errno);
TALLOC_FREE(subreq);
if (rc == -1) {
const char *reason;
reason = talloc_asprintf(call, "dns_tcp_call_writev_done: "
"tstream_writev_queue_recv() - %d:%s",
sys_errno, strerror(sys_errno));
if (!reason) {
reason = "dns_tcp_call_writev_done: tstream_writev_queue_recv() failed";
}
dns_tcp_terminate_connection(call->dns_conn, reason);
return;
}
/* We don't care about errors */
talloc_free(call);
}
/*
called when we get a new connection
*/
static void dns_tcp_accept(struct stream_connection *conn)
{
struct dns_socket *dns_socket;
struct dns_tcp_connection *dns_conn;
struct tevent_req *subreq;
int rc;
dns_conn = talloc_zero(conn, struct dns_tcp_connection);
if (dns_conn == NULL) {
stream_terminate_connection(conn,
"dns_tcp_accept: out of memory");
return;
}
dns_conn->send_queue = tevent_queue_create(conn, "dns_tcp_accept");
if (dns_conn->send_queue == NULL) {
stream_terminate_connection(conn,
"dns_tcp_accept: out of memory");
return;
}
dns_socket = talloc_get_type(conn->private_data, struct dns_socket);
TALLOC_FREE(conn->event.fde);
rc = tstream_bsd_existing_socket(dns_conn,
socket_get_fd(conn->socket),
&dns_conn->tstream);
if (rc < 0) {
stream_terminate_connection(conn,
"dns_tcp_accept: out of memory");
return;
}
dns_conn->conn = conn;
dns_conn->dns_socket = dns_socket;
conn->private_data = dns_conn;
/*
* The dns tcp pdu's has the length as 2 byte (initial_read_size),
* packet_full_request_u16 provides the pdu length then.
*/
subreq = tstream_read_pdu_blob_send(dns_conn,
dns_conn->conn->event.ctx,
dns_conn->tstream,
2, /* initial_read_size */
packet_full_request_u16,
dns_conn);
if (subreq == NULL) {
dns_tcp_terminate_connection(dns_conn, "dns_tcp_accept: "
"no memory for tstream_read_pdu_blob_send");
return;
}
tevent_req_set_callback(subreq, dns_tcp_call_loop, dns_conn);
}
static const struct stream_server_ops dns_tcp_stream_ops = {
.name = "dns_tcp",
.accept_connection = dns_tcp_accept,
.recv_handler = dns_tcp_recv,
.send_handler = dns_tcp_send
};
struct dns_udp_call {
struct dns_udp_socket *sock;
struct tsocket_address *src;
DATA_BLOB in;
DATA_BLOB out;
};
static void dns_udp_call_process_done(struct tevent_req *subreq);
static void dns_udp_call_sendto_done(struct tevent_req *subreq);
static void dns_udp_call_loop(struct tevent_req *subreq)
{
struct dns_udp_socket *sock = tevent_req_callback_data(subreq,
struct dns_udp_socket);
struct dns_server *dns = sock->dns_socket->dns;
struct dns_udp_call *call;
uint8_t *buf;
ssize_t len;
int sys_errno;
call = talloc(sock, struct dns_udp_call);
if (call == NULL) {
talloc_free(call);
goto done;
}
call->sock = sock;
len = tdgram_recvfrom_recv(subreq, &sys_errno,
call, &buf, &call->src);
TALLOC_FREE(subreq);
if (len == -1) {
talloc_free(call);
goto done;
}
call->in.data = buf;
call->in.length = len;
DEBUG(10,("Received DNS UDP packet of length %lu from %s\n",
(long)call->in.length,
tsocket_address_string(call->src, call)));
subreq = dns_process_send(call, dns->task->event_ctx, dns,
call->src,
sock->dns_socket->local_address,
&call->in);
if (subreq == NULL) {
TALLOC_FREE(call);
goto done;
}
tevent_req_set_callback(subreq, dns_udp_call_process_done, call);
done:
subreq = tdgram_recvfrom_send(sock,
sock->dns_socket->dns->task->event_ctx,
sock->dgram);
if (subreq == NULL) {
task_server_terminate(sock->dns_socket->dns->task,
"no memory for tdgram_recvfrom_send",
true);
return;
}
tevent_req_set_callback(subreq, dns_udp_call_loop, sock);
}
static void dns_udp_call_process_done(struct tevent_req *subreq)
{
struct dns_udp_call *call = tevent_req_callback_data(
subreq, struct dns_udp_call);
struct dns_udp_socket *sock = call->sock;
struct dns_server *dns = sock->dns_socket->dns;
WERROR err;
err = dns_process_recv(subreq, call, &call->out);
TALLOC_FREE(subreq);
if (!W_ERROR_IS_OK(err)) {
DEBUG(1, ("dns_process returned %s\n", win_errstr(err)));
TALLOC_FREE(call);
return;
}
subreq = tdgram_sendto_queue_send(call,
dns->task->event_ctx,
sock->dgram,
sock->send_queue,
call->out.data,
call->out.length,
call->src);
if (subreq == NULL) {
talloc_free(call);
return;
}
tevent_req_set_callback(subreq, dns_udp_call_sendto_done, call);
}
static void dns_udp_call_sendto_done(struct tevent_req *subreq)
{
struct dns_udp_call *call = tevent_req_callback_data(subreq,
struct dns_udp_call);
int sys_errno;
tdgram_sendto_queue_recv(subreq, &sys_errno);
/* We don't care about errors */
talloc_free(call);
}
/*
start listening on the given address
*/
static NTSTATUS dns_add_socket(struct dns_server *dns,
const struct model_ops *model_ops,
const char *name,
const char *address,
uint16_t port)
{
struct dns_socket *dns_socket;
struct dns_udp_socket *dns_udp_socket;
struct tevent_req *udpsubreq;
NTSTATUS status;
int ret;
dns_socket = talloc(dns, struct dns_socket);
NT_STATUS_HAVE_NO_MEMORY(dns_socket);
dns_socket->dns = dns;
ret = tsocket_address_inet_from_strings(dns_socket, "ip",
address, port,
&dns_socket->local_address);
if (ret != 0) {
status = map_nt_error_from_unix_common(errno);
return status;
}
status = stream_setup_socket(dns->task,
dns->task->event_ctx,
dns->task->lp_ctx,
model_ops,
&dns_tcp_stream_ops,
"ip", address, &port,
lpcfg_socket_options(dns->task->lp_ctx),
dns_socket,
dns->task->process_context);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
address, port, nt_errstr(status)));
talloc_free(dns_socket);
return status;
}
dns_udp_socket = talloc(dns_socket, struct dns_udp_socket);
NT_STATUS_HAVE_NO_MEMORY(dns_udp_socket);
dns_udp_socket->dns_socket = dns_socket;
ret = tdgram_inet_udp_socket(dns_socket->local_address,
NULL,
dns_udp_socket,
&dns_udp_socket->dgram);
if (ret != 0) {
status = map_nt_error_from_unix_common(errno);
DEBUG(0,("Failed to bind to %s:%u UDP - %s\n",
address, port, nt_errstr(status)));
return status;
}
dns_udp_socket->send_queue = tevent_queue_create(dns_udp_socket,
"dns_udp_send_queue");
NT_STATUS_HAVE_NO_MEMORY(dns_udp_socket->send_queue);
udpsubreq = tdgram_recvfrom_send(dns_udp_socket,
dns->task->event_ctx,
dns_udp_socket->dgram);
NT_STATUS_HAVE_NO_MEMORY(udpsubreq);
tevent_req_set_callback(udpsubreq, dns_udp_call_loop, dns_udp_socket);
return NT_STATUS_OK;
}
/*
setup our listening sockets on the configured network interfaces
*/
static NTSTATUS dns_startup_interfaces(struct dns_server *dns,
struct interface *ifaces,
const struct model_ops *model_ops)
{
size_t num_interfaces;
TALLOC_CTX *tmp_ctx = talloc_new(dns);
NTSTATUS status;
int i;
if (ifaces != NULL) {
num_interfaces = iface_list_count(ifaces);
for (i=0; i<num_interfaces; i++) {
const char *address = talloc_strdup(tmp_ctx,
iface_list_n_ip(ifaces, i));
status = dns_add_socket(dns, model_ops, "dns", address,
lpcfg_dns_port(dns->task->lp_ctx));
NT_STATUS_NOT_OK_RETURN(status);
}
} else {
size_t num_binds = 0;
char **wcard;
wcard = iface_list_wildcard(tmp_ctx);
if (wcard == NULL) {
DEBUG(0, ("No wildcard address available\n"));
return NT_STATUS_INTERNAL_ERROR;
}
for (i = 0; wcard[i] != NULL; i++) {
status = dns_add_socket(dns, model_ops, "dns", wcard[i],
lpcfg_dns_port(dns->task->lp_ctx));
if (NT_STATUS_IS_OK(status)) {
num_binds++;
}
}
if (num_binds == 0) {
talloc_free(tmp_ctx);
return NT_STATUS_INVALID_PARAMETER_MIX;
}
}
talloc_free(tmp_ctx);
return NT_STATUS_OK;
}
static struct dns_server_tkey_store *tkey_store_init(TALLOC_CTX *mem_ctx,
uint16_t size)
{
struct dns_server_tkey_store *buffer = talloc_zero(mem_ctx,
struct dns_server_tkey_store);
if (buffer == NULL) {
return NULL;
}
buffer->size = size;
buffer->next_idx = 0;
buffer->tkeys = talloc_zero_array(buffer, struct dns_server_tkey *, size);
if (buffer->tkeys == NULL) {
TALLOC_FREE(buffer);
}
return buffer;
}
static NTSTATUS dns_server_reload_zones(struct dns_server *dns)
{
NTSTATUS status;
struct dns_server_zone *new_list = NULL;
struct dns_server_zone *old_list = dns->zones;
struct dns_server_zone *old_zone;
status = dns_common_zones(dns->samdb, dns, NULL, &new_list);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
dns->zones = new_list;
while ((old_zone = DLIST_TAIL(old_list)) != NULL) {
DLIST_REMOVE(old_list, old_zone);
talloc_free(old_zone);
}
return NT_STATUS_OK;
}
/**
* Called when the internal DNS server should reload the zones from DB, for
* example, when zones are added or deleted through RPC or replicated by
* inbound DRS.
*/
static NTSTATUS dns_reload_zones(struct irpc_message *msg,
struct dnssrv_reload_dns_zones *r)
{
struct dns_server *dns;
dns = talloc_get_type(msg->private_data, struct dns_server);
if (dns == NULL) {
r->out.result = NT_STATUS_INTERNAL_ERROR;
return NT_STATUS_INTERNAL_ERROR;
}
r->out.result = dns_server_reload_zones(dns);
return NT_STATUS_OK;
}
static NTSTATUS dns_task_init(struct task_server *task)
{
struct dns_server *dns;
NTSTATUS status;
struct interface *ifaces = NULL;
int ret;
static const char * const attrs_none[] = { NULL};
struct ldb_message *dns_acc;
char *hostname_lower;
char *dns_spn;
bool ok;
switch (lpcfg_server_role(task->lp_ctx)) {
case ROLE_STANDALONE:
task_server_terminate(task, "dns: no DNS required in standalone configuration", false);
return NT_STATUS_INVALID_DOMAIN_ROLE;
case ROLE_DOMAIN_MEMBER:
task_server_terminate(task, "dns: no DNS required in member server configuration", false);
return NT_STATUS_INVALID_DOMAIN_ROLE;
case ROLE_ACTIVE_DIRECTORY_DC:
/* Yes, we want a DNS */
break;
}
if (lpcfg_interfaces(task->lp_ctx) && lpcfg_bind_interfaces_only(task->lp_ctx)) {
load_interface_list(task, task->lp_ctx, &ifaces);
if (iface_list_count(ifaces) == 0) {
task_server_terminate(task, "dns: no network interfaces configured", false);
return NT_STATUS_UNSUCCESSFUL;
}
}
task_server_set_title(task, "task[dns]");
dns = talloc_zero(task, struct dns_server);
if (dns == NULL) {
task_server_terminate(task, "dns: out of memory", true);
return NT_STATUS_NO_MEMORY;
}
dns->task = task;
dns->server_credentials = cli_credentials_init(dns);
if (!dns->server_credentials) {
task_server_terminate(task, "Failed to init server credentials\n", true);
return NT_STATUS_UNSUCCESSFUL;
}
dns->samdb = samdb_connect(dns,
dns->task->event_ctx,
dns->task->lp_ctx,
system_session(dns->task->lp_ctx),
NULL,
0);
if (!dns->samdb) {
task_server_terminate(task, "dns: samdb_connect failed", true);
return NT_STATUS_UNSUCCESSFUL;
}
ok = cli_credentials_set_conf(dns->server_credentials, task->lp_ctx);
if (!ok) {
task_server_terminate(task,
"dns: failed to load smb.conf",
true);
return NT_STATUS_UNSUCCESSFUL;
}
hostname_lower = strlower_talloc(dns, lpcfg_netbios_name(task->lp_ctx));
dns_spn = talloc_asprintf(dns, "DNS/%s.%s",
hostname_lower,
lpcfg_dnsdomain(task->lp_ctx));
TALLOC_FREE(hostname_lower);
ret = dsdb_search_one(dns->samdb, dns, &dns_acc,
ldb_get_default_basedn(dns->samdb), LDB_SCOPE_SUBTREE,
attrs_none, 0, "(servicePrincipalName=%s)",
dns_spn);
if (ret == LDB_SUCCESS) {
TALLOC_FREE(dns_acc);
if (!dns_spn) {
task_server_terminate(task, "dns: talloc_asprintf failed", true);
return NT_STATUS_UNSUCCESSFUL;
}
status = cli_credentials_set_stored_principal(dns->server_credentials, task->lp_ctx, dns_spn);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task,
talloc_asprintf(task, "Failed to obtain server credentials for DNS, "
"despite finding it in the samdb! %s\n",
nt_errstr(status)),
true);
return status;
}
} else {
TALLOC_FREE(dns_spn);
status = cli_credentials_set_machine_account(dns->server_credentials, task->lp_ctx);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task,
talloc_asprintf(task, "Failed to obtain server credentials, perhaps a standalone server?: %s\n",
nt_errstr(status)),
true);
return status;
}
}
dns->tkeys = tkey_store_init(dns, TKEY_BUFFER_SIZE);
if (!dns->tkeys) {
task_server_terminate(task, "Failed to allocate tkey storage\n", true);
return NT_STATUS_NO_MEMORY;
}
status = dns_server_reload_zones(dns);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "dns: failed to load DNS zones", true);
return status;
}
status = dns_startup_interfaces(dns, ifaces, task->model_ops);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "dns failed to setup interfaces", true);
return status;
}
/* Setup the IRPC interface and register handlers */
status = irpc_add_name(task->msg_ctx, "dnssrv");
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "dns: failed to register IRPC name", true);
return status;
}
status = IRPC_REGISTER(task->msg_ctx, irpc, DNSSRV_RELOAD_DNS_ZONES,
dns_reload_zones, dns);
if (!NT_STATUS_IS_OK(status)) {
task_server_terminate(task, "dns: failed to setup reload handler", true);
return status;
}
return NT_STATUS_OK;
}
NTSTATUS server_service_dns_init(TALLOC_CTX *ctx)
{
static const struct service_details details = {
.inhibit_fork_on_accept = true,
.inhibit_pre_fork = true,
.task_init = dns_task_init,
.post_fork = NULL
};
return register_server_service(ctx, "dns", &details);
}