2010-10-12 08:23:32 +04:00
/*
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"
2020-11-20 17:27:17 +03:00
# include "samba/service_task.h"
# include "samba/service.h"
# include "samba/service_stream.h"
# include "samba/process_model.h"
2010-10-12 08:23:32 +04:00
# include "lib/events/events.h"
# include "lib/socket/socket.h"
# include "lib/tsocket/tsocket.h"
# include "libcli/util/tstream.h"
2010-09-30 04:24:53 +04:00
# include "libcli/util/ntstatus.h"
2010-10-12 08:23:32 +04:00
# include "system/network.h"
# include "lib/stream/packet.h"
# include "lib/socket/netif.h"
# include "dns_server/dns_server.h"
# include "param/param.h"
2010-10-22 11:37:38 +04:00
# include "librpc/ndr/libndr.h"
# include "librpc/gen_ndr/ndr_dns.h"
2010-10-12 00:32:25 +04:00
# include "librpc/gen_ndr/ndr_dnsp.h"
# include <ldb.h>
# include "dsdb/samdb/samdb.h"
2010-10-01 03:35:04 +04:00
# include "dsdb/common/util.h"
2010-10-12 00:32:25 +04:00
# include "auth/session.h"
2010-10-01 03:35:04 +04:00
# include "lib/util/dlinklist.h"
2012-05-24 19:02:57 +04:00
# include "lib/util/tevent_werror.h"
2012-08-30 11:04:07 +04:00
# include "auth/auth.h"
# include "auth/credentials/credentials.h"
2014-12-16 12:58:50 +03:00
# include "librpc/gen_ndr/ndr_irpc.h"
# include "lib/messaging/irpc.h"
2015-12-28 22:01:54 +03:00
# include "libds/common/roles.h"
2010-10-12 08:23:32 +04:00
2013-01-14 04:14:29 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_DNS
2017-04-20 22:24:43 +03:00
NTSTATUS server_service_dns_init ( TALLOC_CTX * ) ;
2011-03-19 02:45:45 +03:00
2010-10-12 08:23:32 +04:00
/* 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 " ) ;
}
2012-05-24 19:02:57 +04:00
struct dns_process_state {
DATA_BLOB * in ;
2012-09-05 10:34:04 +04:00
struct dns_server * dns ;
2012-05-24 19:02:57 +04:00
struct dns_name_packet in_packet ;
struct dns_request_state state ;
2022-07-14 13:00:51 +03:00
WERROR dns_err ;
2012-05-24 19:02:57 +04:00
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 ,
2017-03-01 04:19:50 +03:00
const struct tsocket_address * remote_address ,
const struct tsocket_address * local_address ,
2012-05-24 19:02:57 +04:00
DATA_BLOB * in )
2010-10-12 08:23:32 +04:00
{
2012-05-24 19:02:57 +04:00
struct tevent_req * req , * subreq ;
struct dns_process_state * state ;
2010-10-22 11:37:38 +04:00
enum ndr_err_code ndr_err ;
2010-10-12 01:39:44 +04:00
WERROR ret ;
2016-02-17 01:30:21 +03:00
const char * * forwarder = lpcfg_dns_forwarder ( dns - > task - > lp_ctx ) ;
2012-05-24 19:02:57 +04:00
req = tevent_req_create ( mem_ctx , & state , struct dns_process_state ) ;
if ( req = = NULL ) {
return NULL ;
2010-10-01 07:35:00 +04:00
}
2014-10-14 11:34:29 +04:00
state - > state . mem_ctx = state ;
2012-05-24 19:02:57 +04:00
state - > in = in ;
2010-10-01 07:35:00 +04:00
2012-09-05 10:34:04 +04:00
state - > dns = dns ;
2012-05-24 19:02:57 +04:00
if ( in - > length < 12 ) {
2015-12-03 17:24:26 +03:00
tevent_req_werror ( req , WERR_INVALID_PARAMETER ) ;
2012-05-24 19:02:57 +04:00
return tevent_req_post ( req , ev ) ;
2012-05-24 17:59:32 +04:00
}
2013-01-18 21:36:45 +04:00
dump_data_dbgc ( DBGC_DNS , 8 , in - > data , in - > length ) ;
2010-10-22 11:37:38 +04:00
2012-05-24 19:02:57 +04:00
ndr_err = ndr_pull_struct_blob (
in , state , & state - > in_packet ,
( ndr_pull_flags_fn_t ) ndr_pull_dns_name_packet ) ;
2010-10-22 11:37:38 +04:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2022-07-14 13:00:51 +03:00
DBG_NOTICE ( " ndr_pull_dns_name_packet() failed with %s \n " ,
ndr_map_error2string ( ndr_err ) ) ;
2022-07-14 13:00:51 +03:00
state - > dns_err = DNS_ERR ( FORMAT_ERROR ) ;
2012-05-24 19:02:57 +04:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2010-10-22 11:37:38 +04:00
}
2013-01-18 21:36:45 +04:00
if ( DEBUGLVLC ( DBGC_DNS , 8 ) ) {
NDR_PRINT_DEBUGC ( DBGC_DNS , dns_name_packet , & state - > in_packet ) ;
2011-11-15 11:38:27 +04:00
}
2012-05-24 19:02:57 +04:00
2014-05-13 10:13:29 +04:00
if ( state - > in_packet . operation & DNS_FLAG_REPLY ) {
2022-07-14 13:00:51 +03:00
DBG_INFO ( " Won't reply to replies. \n " ) ;
2015-12-03 17:24:26 +03:00
tevent_req_werror ( req , WERR_INVALID_PARAMETER ) ;
2014-05-13 10:13:29 +04:00
return tevent_req_post ( req , ev ) ;
}
2012-05-24 19:02:57 +04:00
state - > state . flags = state - > in_packet . operation ;
state - > state . flags | = DNS_FLAG_REPLY ;
2012-03-27 15:59:03 +04:00
2017-03-01 04:19:50 +03:00
state - > state . local_address = local_address ;
state - > state . remote_address = remote_address ;
2016-02-17 01:30:21 +03:00
if ( forwarder & & * forwarder & & * * forwarder ) {
2012-05-24 19:02:57 +04:00
state - > state . flags | = DNS_FLAG_RECURSION_AVAIL ;
2012-03-27 16:42:15 +04:00
}
2010-09-30 04:24:53 +04:00
2012-05-24 19:02:57 +04:00
state - > out_packet = state - > in_packet ;
2010-09-30 04:24:53 +04:00
2017-03-01 04:19:50 +03:00
ret = dns_verify_tsig ( dns , state , & state - > state ,
& state - > out_packet , in ) ;
2016-05-30 17:42:14 +03:00
if ( ! W_ERROR_IS_OK ( ret ) ) {
2022-07-14 13:00:51 +03:00
DBG_INFO ( " dns_verify_tsig() failed with %s \n " ,
win_errstr ( ret ) ) ;
2022-07-14 13:00:51 +03:00
state - > dns_err = ret ;
2016-05-30 17:42:14 +03:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2012-05-24 19:02:57 +04:00
switch ( state - > in_packet . operation & DNS_OPCODE ) {
case DNS_OPCODE_QUERY :
subreq = dns_server_process_query_send (
2017-03-01 04:19:50 +03:00
state , ev , dns ,
& state - > state , & state - > in_packet ) ;
2012-05-24 19:02:57 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , dns_process_done , req ) ;
return req ;
2011-09-28 06:51:55 +04:00
case DNS_OPCODE_UPDATE :
2012-05-24 19:02:57 +04:00
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 ) ;
2022-07-14 13:00:51 +03:00
DBG_DEBUG ( " dns_server_process_update(): %s \n " ,
win_errstr ( ret ) ) ;
2010-10-01 05:21:53 +04:00
break ;
default :
2010-10-12 01:39:44 +04:00
ret = WERR_DNS_ERROR_RCODE_NOT_IMPLEMENTED ;
2022-07-14 13:00:51 +03:00
DBG_NOTICE ( " OPCODE[0x%x]: %s \n " ,
( state - > in_packet . operation & DNS_OPCODE ) ,
win_errstr ( ret ) ) ;
2010-10-01 05:21:53 +04:00
}
2022-07-14 13:00:51 +03:00
state - > dns_err = ret ;
2012-05-24 19:02:57 +04:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2010-09-30 04:24:53 +04:00
2012-05-24 19:02:57 +04:00
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 ;
2010-09-30 04:24:53 +04:00
2012-05-24 19:02:57 +04:00
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 ) ;
2010-09-30 04:24:53 +04:00
2022-07-14 13:00:51 +03:00
DBG_DEBUG ( " dns_server_process_query_recv(): %s \n " ,
win_errstr ( ret ) ) ;
2022-07-14 13:00:51 +03:00
state - > dns_err = ret ;
2012-05-24 19:02:57 +04:00
tevent_req_done ( req ) ;
}
2010-09-30 04:24:53 +04:00
2012-05-24 19:02:57 +04:00
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 ;
2022-07-14 13:00:51 +03:00
uint16_t dns_err ;
2012-05-24 19:02:57 +04:00
WERROR ret ;
2012-03-27 15:59:03 +04:00
2012-05-24 19:02:57 +04:00
if ( tevent_req_is_werror ( req , & ret ) ) {
2022-07-14 13:00:51 +03:00
DBG_NOTICE ( " ERROR: %s from %s \n " , win_errstr ( ret ) ,
tevent_req_print ( state , req ) ) ;
2012-05-24 19:02:57 +04:00
return ret ;
2011-11-15 11:38:27 +04:00
}
2022-07-14 13:00:51 +03:00
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 ) )
2016-05-30 17:44:00 +03:00
{
2022-07-14 13:00:51 +03:00
DBG_INFO ( " FAILURE: %s from %s \n " ,
win_errstr ( state - > dns_err ) ,
tevent_req_print ( state , req ) ) ;
2012-03-28 14:19:51 +04:00
goto drop ;
2010-09-30 04:24:53 +04:00
}
2022-07-14 13:00:51 +03:00
if ( dns_err ! = DNS_RCODE_OK ) {
2022-07-14 13:00:51 +03:00
DBG_DEBUG ( " INFO: %s from %s \n " ,
win_errstr ( state - > dns_err ) ,
tevent_req_print ( state , req ) ) ;
2022-07-14 13:00:51 +03:00
state - > out_packet . operation | = dns_err ;
2022-07-14 13:00:51 +03:00
} else {
DBG_DEBUG ( " OK: %s \n " ,
tevent_req_print ( state , req ) ) ;
2015-07-17 16:27:51 +03:00
}
2012-05-24 19:02:57 +04:00
state - > out_packet . operation | = state - > state . flags ;
2010-09-30 04:24:53 +04:00
2012-09-05 10:34:04 +04:00
if ( state - > state . sign ) {
ret = dns_sign_tsig ( state - > dns , mem_ctx , & state - > state ,
& state - > out_packet , 0 ) ;
if ( ! W_ERROR_IS_OK ( ret ) ) {
2022-07-14 13:00:51 +03:00
DBG_WARNING ( " dns_sign_tsig() failed %s \n " ,
win_errstr ( ret ) ) ;
2022-07-14 13:00:51 +03:00
dns_err = DNS_RCODE_SERVFAIL ;
2012-09-05 10:34:04 +04:00
goto drop ;
}
}
2013-01-18 21:36:45 +04:00
if ( DEBUGLVLC ( DBGC_DNS , 8 ) ) {
NDR_PRINT_DEBUGC ( DBGC_DNS , dns_name_packet , & state - > out_packet ) ;
2013-01-14 03:55:37 +04:00
}
2012-05-24 19:02:57 +04:00
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 ) ) {
2022-07-14 13:00:51 +03:00
DBG_WARNING ( " Failed to push packet: %s! \n " ,
ndr_errstr ( ndr_err ) ) ;
2022-07-14 13:00:51 +03:00
dns_err = DNS_RCODE_SERVFAIL ;
2012-05-24 19:02:57 +04:00
goto drop ;
}
return WERR_OK ;
2012-03-28 14:19:51 +04:00
drop :
2012-05-24 19:02:57 +04:00
* out = data_blob_talloc ( mem_ctx , state - > in - > data , state - > in - > length ) ;
if ( out - > data = = NULL ) {
2015-12-03 17:24:18 +03:00
return WERR_NOT_ENOUGH_MEMORY ;
2012-05-24 19:02:57 +04:00
}
2012-03-28 14:19:51 +04:00
out - > data [ 2 ] | = 0x80 ; /* Toggle DNS_FLAG_REPLY */
2022-07-14 13:00:51 +03:00
out - > data [ 3 ] | = dns_err ;
2012-05-24 19:02:57 +04:00
return WERR_OK ;
}
2010-10-12 08:23:32 +04:00
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 ] ;
} ;
2012-05-28 20:42:54 +04:00
static void dns_tcp_call_process_done ( struct tevent_req * subreq ) ;
2010-10-12 08:23:32 +04:00
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 ) ;
2012-05-28 20:42:54 +04:00
struct dns_server * dns = dns_conn - > dns_socket - > dns ;
2010-10-12 08:23:32 +04:00
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 ;
}
2012-03-06 11:49:16 +04:00
DEBUG ( 10 , ( " Received DNS TCP packet of length %lu from %s \n " ,
2010-10-12 08:23:32 +04:00
( long ) call - > in . length ,
tsocket_address_string ( dns_conn - > conn - > remote_address , call ) ) ) ;
/* skip length header */
2012-03-06 11:49:16 +04:00
call - > in . data + = 2 ;
call - > in . length - = 2 ;
2010-10-12 08:23:32 +04:00
2012-05-28 20:42:54 +04:00
subreq = dns_process_send ( call , dns - > task - > event_ctx , dns ,
2017-03-01 04:19:50 +03:00
dns_conn - > conn - > remote_address ,
dns_conn - > conn - > local_address ,
2012-05-28 20:42:54 +04:00
& 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 ) ;
2012-05-24 19:02:57 +04:00
if ( ! W_ERROR_IS_OK ( err ) ) {
DEBUG ( 1 , ( " dns_process returned %s \n " , win_errstr ( err ) ) ) ;
2010-10-12 08:23:32 +04:00
dns_tcp_terminate_connection ( dns_conn ,
" dns_tcp_call_loop: process function failed " ) ;
return ;
}
/* First add the length of the out buffer */
2012-03-06 11:49:16 +04:00
RSSVAL ( call - > out_hdr , 0 , call - > out . length ) ;
2010-10-12 08:23:32 +04:00
call - > out_iov [ 0 ] . iov_base = ( char * ) call - > out_hdr ;
2012-03-06 11:49:16 +04:00
call - > out_iov [ 0 ] . iov_len = 2 ;
2010-10-12 08:23:32 +04:00
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 ;
/*
2012-03-06 11:49:16 +04:00
* The dns tcp pdu ' s has the length as 2 byte ( initial_read_size ) ,
* packet_full_request_u16 provides the pdu length then .
2010-10-12 08:23:32 +04:00
*/
subreq = tstream_read_pdu_blob_send ( dns_conn ,
dns_conn - > conn - > event . ctx ,
dns_conn - > tstream ,
2012-03-06 11:49:16 +04:00
2 , /* initial_read_size */
packet_full_request_u16 ,
2010-10-12 08:23:32 +04:00
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 {
2012-05-28 20:42:54 +04:00
struct dns_udp_socket * sock ;
2010-10-12 08:23:32 +04:00
struct tsocket_address * src ;
DATA_BLOB in ;
DATA_BLOB out ;
} ;
2012-05-28 20:42:54 +04:00
static void dns_udp_call_process_done ( struct tevent_req * subreq ) ;
2010-10-12 08:23:32 +04:00
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 ) ;
2012-05-28 20:42:54 +04:00
struct dns_server * dns = sock - > dns_socket - > dns ;
2010-10-12 08:23:32 +04:00
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 ;
}
2012-05-28 20:42:54 +04:00
call - > sock = sock ;
2010-10-12 08:23:32 +04:00
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 ;
2011-12-16 12:13:31 +04:00
DEBUG ( 10 , ( " Received DNS UDP packet of length %lu from %s \n " ,
2010-10-12 08:23:32 +04:00
( long ) call - > in . length ,
tsocket_address_string ( call - > src , call ) ) ) ;
2012-05-28 20:42:54 +04:00
subreq = dns_process_send ( call , dns - > task - > event_ctx , dns ,
2017-03-01 04:19:50 +03:00
call - > src ,
sock - > dns_socket - > local_address ,
2012-05-28 20:42:54 +04:00
& call - > in ) ;
2010-10-12 08:23:32 +04:00
if ( subreq = = NULL ) {
2012-05-28 20:42:54 +04:00
TALLOC_FREE ( call ) ;
2010-10-12 08:23:32 +04:00
goto done ;
}
2012-05-28 20:42:54 +04:00
tevent_req_set_callback ( subreq , dns_udp_call_process_done , call ) ;
2010-10-12 08:23:32 +04:00
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 ) ;
}
2012-05-28 20:42:54 +04:00
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 ) ;
}
2010-10-12 08:23:32 +04:00
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 ;
2012-10-03 11:37:34 +04:00
tdgram_sendto_queue_recv ( subreq , & sys_errno ) ;
2010-10-12 08:23:32 +04:00
/* 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 ) {
2011-06-20 08:55:32 +04:00
status = map_nt_error_from_unix_common ( errno ) ;
2010-10-12 08:23:32 +04:00
return status ;
}
2010-11-15 02:12:22 +03:00
status = stream_setup_socket ( dns - > task ,
dns - > task - > event_ctx ,
2010-10-12 08:23:32 +04:00
dns - > task - > lp_ctx ,
model_ops ,
& dns_tcp_stream_ops ,
" ip " , address , & port ,
lpcfg_socket_options ( dns - > task - > lp_ctx ) ,
2017-09-14 22:09:23 +03:00
dns_socket ,
dns - > task - > process_context ) ;
2010-10-12 08:23:32 +04:00
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 ) {
2011-06-20 08:55:32 +04:00
status = map_nt_error_from_unix_common ( errno ) ;
2010-10-12 08:23:32 +04:00
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
*/
2014-02-27 13:28:23 +04:00
static NTSTATUS dns_startup_interfaces ( struct dns_server * dns ,
2017-09-18 04:05:24 +03:00
struct interface * ifaces ,
const struct model_ops * model_ops )
2010-10-12 08:23:32 +04:00
{
2017-12-07 19:40:00 +03:00
size_t num_interfaces ;
2010-10-12 08:23:32 +04:00
TALLOC_CTX * tmp_ctx = talloc_new ( dns ) ;
NTSTATUS status ;
int i ;
2012-08-12 20:08:20 +04:00
if ( ifaces ! = NULL ) {
num_interfaces = iface_list_count ( ifaces ) ;
2010-10-12 08:23:32 +04:00
2012-08-12 20:08:20 +04:00
for ( i = 0 ; i < num_interfaces ; i + + ) {
const char * address = talloc_strdup ( tmp_ctx ,
iface_list_n_ip ( ifaces , i ) ) ;
2010-10-12 08:23:32 +04:00
2012-08-12 20:08:20 +04:00
status = dns_add_socket ( dns , model_ops , " dns " , address ,
2021-09-22 20:09:02 +03:00
lpcfg_dns_port ( dns - > task - > lp_ctx ) ) ;
2012-08-12 20:08:20 +04:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
}
} else {
2017-12-07 19:40:00 +03:00
size_t num_binds = 0 ;
2014-02-27 13:28:23 +04:00
char * * wcard ;
wcard = iface_list_wildcard ( tmp_ctx ) ;
2012-08-12 20:08:20 +04:00
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 ] ,
2021-09-22 20:09:02 +03:00
lpcfg_dns_port ( dns - > task - > lp_ctx ) ) ;
2014-06-05 23:32:30 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
num_binds + + ;
}
}
if ( num_binds = = 0 ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_INVALID_PARAMETER_MIX ;
2012-08-12 20:08:20 +04:00
}
2010-10-12 08:23:32 +04:00
}
talloc_free ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
2010-10-01 03:35:04 +04:00
2012-08-31 15:41:19 +04:00
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 ;
}
2014-12-16 12:58:50 +03:00
static NTSTATUS dns_server_reload_zones ( struct dns_server * dns )
{
2015-09-22 03:10:00 +03:00
NTSTATUS status ;
2014-12-16 12:58:50 +03:00
struct dns_server_zone * new_list = NULL ;
2021-03-31 00:47:05 +03:00
struct dns_server_zone * old_list = dns - > zones ;
2014-12-16 12:58:50 +03:00
struct dns_server_zone * old_zone ;
2017-06-09 07:05:31 +03:00
status = dns_common_zones ( dns - > samdb , dns , NULL , & new_list ) ;
2015-09-22 03:10:00 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2014-12-16 12:58:50 +03:00
}
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 ;
}
2018-08-23 00:35:52 +03:00
static NTSTATUS dns_task_init ( struct task_server * task )
2010-10-12 08:23:32 +04:00
{
struct dns_server * dns ;
NTSTATUS status ;
2012-08-12 20:08:20 +04:00
struct interface * ifaces = NULL ;
2010-10-01 03:35:04 +04:00
int ret ;
2012-10-16 08:08:30 +04:00
static const char * const attrs_none [ ] = { NULL } ;
struct ldb_message * dns_acc ;
char * hostname_lower ;
char * dns_spn ;
2021-06-22 10:46:14 +03:00
bool ok ;
2010-10-12 08:23:32 +04:00
switch ( lpcfg_server_role ( task - > lp_ctx ) ) {
case ROLE_STANDALONE :
task_server_terminate ( task , " dns: no DNS required in standalone configuration " , false ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_INVALID_DOMAIN_ROLE ;
2010-10-12 08:23:32 +04:00
case ROLE_DOMAIN_MEMBER :
task_server_terminate ( task , " dns: no DNS required in member server configuration " , false ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_INVALID_DOMAIN_ROLE ;
2012-06-10 16:08:20 +04:00
case ROLE_ACTIVE_DIRECTORY_DC :
2010-10-12 08:23:32 +04:00
/* Yes, we want a DNS */
break ;
}
2012-08-12 20:08:20 +04:00
if ( lpcfg_interfaces ( task - > lp_ctx ) & & lpcfg_bind_interfaces_only ( task - > lp_ctx ) ) {
load_interface_list ( task , task - > lp_ctx , & ifaces ) ;
2010-10-12 08:23:32 +04:00
2012-08-12 20:08:20 +04:00
if ( iface_list_count ( ifaces ) = = 0 ) {
task_server_terminate ( task , " dns: no network interfaces configured " , false ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_UNSUCCESSFUL ;
2012-08-12 20:08:20 +04:00
}
2010-10-12 08:23:32 +04:00
}
task_server_set_title ( task , " task[dns] " ) ;
2010-10-01 03:35:04 +04:00
dns = talloc_zero ( task , struct dns_server ) ;
2010-10-12 08:23:32 +04:00
if ( dns = = NULL ) {
task_server_terminate ( task , " dns: out of memory " , true ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_NO_MEMORY ;
2010-10-12 08:23:32 +04:00
}
dns - > task = task ;
2012-08-30 11:04:07 +04:00
dns - > server_credentials = cli_credentials_init ( dns ) ;
if ( ! dns - > server_credentials ) {
task_server_terminate ( task , " Failed to init server credentials \n " , true ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_UNSUCCESSFUL ;
2012-08-30 11:04:07 +04:00
}
2018-04-11 21:41:30 +03:00
dns - > samdb = samdb_connect ( dns ,
dns - > task - > event_ctx ,
dns - > task - > lp_ctx ,
system_session ( dns - > task - > lp_ctx ) ,
NULL ,
0 ) ;
2012-10-16 08:08:30 +04:00
if ( ! dns - > samdb ) {
task_server_terminate ( task , " dns: samdb_connect failed " , true ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_UNSUCCESSFUL ;
2012-08-30 11:04:07 +04:00
}
2021-06-22 10:46:14 +03:00
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 ;
}
2012-10-16 08:08:30 +04:00
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 ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_UNSUCCESSFUL ;
2012-10-16 08:08:30 +04:00
}
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 ) ;
2018-08-23 00:35:52 +03:00
return status ;
2012-10-16 08:08:30 +04:00
}
} 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 ) ;
2018-08-23 00:35:52 +03:00
return status ;
2012-10-16 08:08:30 +04:00
}
}
2012-08-31 15:41:19 +04:00
dns - > tkeys = tkey_store_init ( dns , TKEY_BUFFER_SIZE ) ;
if ( ! dns - > tkeys ) {
task_server_terminate ( task , " Failed to allocate tkey storage \n " , true ) ;
2018-08-23 00:35:52 +03:00
return NT_STATUS_NO_MEMORY ;
2012-08-31 15:41:19 +04:00
}
2014-12-16 12:58:50 +03:00
status = dns_server_reload_zones ( dns ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
task_server_terminate ( task , " dns: failed to load DNS zones " , true ) ;
2018-08-23 00:35:52 +03:00
return status ;
2010-10-01 03:35:04 +04:00
}
2017-09-18 04:05:24 +03:00
status = dns_startup_interfaces ( dns , ifaces , task - > model_ops ) ;
2014-12-16 12:58:50 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
task_server_terminate ( task , " dns failed to setup interfaces " , true ) ;
2018-08-23 00:35:52 +03:00
return status ;
2014-12-16 12:58:50 +03:00
}
2010-10-01 03:35:04 +04:00
2014-12-16 12:58:50 +03:00
/* 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 ) ;
2018-08-23 00:35:52 +03:00
return status ;
2010-10-01 03:35:04 +04:00
}
2014-12-16 12:58:50 +03:00
status = IRPC_REGISTER ( task - > msg_ctx , irpc , DNSSRV_RELOAD_DNS_ZONES ,
dns_reload_zones , dns ) ;
2010-10-12 08:23:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-12-16 12:58:50 +03:00
task_server_terminate ( task , " dns: failed to setup reload handler " , true ) ;
2018-08-23 00:35:52 +03:00
return status ;
2010-10-12 08:23:32 +04:00
}
2018-08-23 00:35:52 +03:00
return NT_STATUS_OK ;
2010-10-12 08:23:32 +04:00
}
2017-04-20 22:24:43 +03:00
NTSTATUS server_service_dns_init ( TALLOC_CTX * ctx )
2010-10-12 08:23:32 +04:00
{
2018-08-23 00:29:56 +03:00
static const struct service_details details = {
2017-09-14 22:09:23 +03:00
. inhibit_fork_on_accept = true ,
. inhibit_pre_fork = true ,
2018-08-23 00:35:52 +03:00
. task_init = dns_task_init ,
. post_fork = NULL
2017-09-14 22:09:23 +03:00
} ;
2018-08-23 00:35:52 +03:00
return register_server_service ( ctx , " dns " , & details ) ;
2010-10-12 08:23:32 +04:00
}