2010-10-01 23:59:22 +04:00
/*
Unix SMB / CIFS implementation .
DNS server handler for queries
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"
2010-10-12 01:39:44 +04:00
# include "libcli/util/werror.h"
2010-10-01 23:59:22 +04:00
# 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 "dns_server/dns_server.h"
2012-03-27 10:42:22 +04:00
# include "libcli/dns/libdns.h"
2010-10-01 23:59:22 +04:00
2011-09-28 04:36:42 +04:00
static WERROR create_response_rr ( const struct dns_name_question * question ,
const struct dnsp_DnssrvRpcRecord * rec ,
struct dns_res_rec * * answers , uint16_t * ancount )
{
struct dns_res_rec * ans = * answers ;
uint16_t ai = * ancount ;
2012-03-11 02:47:29 +04:00
char * tmp ;
uint32_t i ;
2011-09-28 04:36:42 +04:00
ZERO_STRUCT ( ans [ ai ] ) ;
switch ( rec - > wType ) {
case DNS_QTYPE_CNAME :
ans [ ai ] . rdata . cname_record = talloc_strdup ( ans , rec - > data . cname ) ;
break ;
case DNS_QTYPE_A :
ans [ ai ] . rdata . ipv4_record = talloc_strdup ( ans , rec - > data . ipv4 ) ;
break ;
case DNS_QTYPE_AAAA :
ans [ ai ] . rdata . ipv6_record = rec - > data . ipv6 ;
break ;
case DNS_TYPE_NS :
ans [ ai ] . rdata . ns_record = rec - > data . ns ;
break ;
case DNS_QTYPE_SRV :
ans [ ai ] . rdata . srv_record . priority = rec - > data . srv . wPriority ;
ans [ ai ] . rdata . srv_record . weight = rec - > data . srv . wWeight ;
ans [ ai ] . rdata . srv_record . port = rec - > data . srv . wPort ;
ans [ ai ] . rdata . srv_record . target = rec - > data . srv . nameTarget ;
break ;
case DNS_QTYPE_SOA :
ans [ ai ] . rdata . soa_record . mname = rec - > data . soa . mname ;
ans [ ai ] . rdata . soa_record . rname = rec - > data . soa . rname ;
ans [ ai ] . rdata . soa_record . serial = rec - > data . soa . serial ;
ans [ ai ] . rdata . soa_record . refresh = rec - > data . soa . refresh ;
ans [ ai ] . rdata . soa_record . retry = rec - > data . soa . retry ;
ans [ ai ] . rdata . soa_record . expire = rec - > data . soa . expire ;
ans [ ai ] . rdata . soa_record . minimum = rec - > data . soa . minimum ;
break ;
2012-03-09 13:43:27 +04:00
case DNS_QTYPE_PTR :
ans [ ai ] . rdata . ptr_record = talloc_strdup ( ans , rec - > data . ptr ) ;
break ;
2012-03-11 02:47:29 +04:00
case DNS_QTYPE_TXT :
tmp = talloc_asprintf ( ans , " \" %s \" " , rec - > data . txt . str [ 0 ] ) ;
for ( i = 1 ; i < rec - > data . txt . count ; i + + ) {
tmp = talloc_asprintf_append ( tmp , " \" %s \" " ,
rec - > data . txt . str [ i ] ) ;
}
ans [ ai ] . rdata . txt_record . txt = tmp ;
break ;
2011-09-28 04:36:42 +04:00
default :
2012-03-11 02:47:29 +04:00
DEBUG ( 0 , ( " Got unhandled type %u query. \n " , rec - > wType ) ) ;
2011-09-28 04:36:42 +04:00
return DNS_ERR ( NOT_IMPLEMENTED ) ;
}
ans [ ai ] . name = talloc_strdup ( ans , question - > name ) ;
ans [ ai ] . rr_type = rec - > wType ;
ans [ ai ] . rr_class = DNS_QCLASS_IN ;
ans [ ai ] . ttl = rec - > dwTtlSeconds ;
ans [ ai ] . length = UINT16_MAX ;
ai + + ;
* answers = ans ;
* ancount = ai ;
return WERR_OK ;
}
2012-03-27 10:42:22 +04:00
static WERROR ask_forwarder ( TALLOC_CTX * mem_ctx ,
struct dns_name_question * question ,
struct dns_res_rec * * answers , uint16_t * ancount ,
struct dns_res_rec * * nsrecs , uint16_t * nscount ,
struct dns_res_rec * * additional , uint16_t * arcount )
{
struct tevent_context * ev = tevent_context_init ( mem_ctx ) ;
struct dns_name_packet * out_packet , * in_packet ;
uint16_t id = random ( ) ;
DATA_BLOB out , in ;
enum ndr_err_code ndr_err ;
WERROR werr = WERR_OK ;
struct tevent_req * req ;
out_packet = talloc_zero ( mem_ctx , struct dns_name_packet ) ;
W_ERROR_HAVE_NO_MEMORY ( out_packet ) ;
out_packet - > id = id ;
out_packet - > operation | = DNS_OPCODE_QUERY | DNS_FLAG_RECURSION_DESIRED ;
out_packet - > qdcount = 1 ;
out_packet - > questions = question ;
ndr_err = ndr_push_struct_blob ( & out , mem_ctx , out_packet ,
( ndr_push_flags_fn_t ) ndr_push_dns_name_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return DNS_ERR ( SERVER_FAILURE ) ;
}
/* FIXME: Don't hardcode this, get that from a configuration somewhere */
req = dns_udp_request_send ( mem_ctx , ev , " 127.0.0.1 " , out . data , out . length ) ;
W_ERROR_HAVE_NO_MEMORY ( req ) ;
if ( ! tevent_req_poll ( req , ev ) ) {
return DNS_ERR ( SERVER_FAILURE ) ;
}
werr = dns_udp_request_recv ( req , mem_ctx , & in . data , & in . length ) ;
W_ERROR_NOT_OK_RETURN ( werr ) ;
in_packet = talloc_zero ( mem_ctx , struct dns_name_packet ) ;
W_ERROR_HAVE_NO_MEMORY ( in_packet ) ;
ndr_err = ndr_pull_struct_blob ( & in , in_packet , in_packet ,
( ndr_pull_flags_fn_t ) ndr_pull_dns_name_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return DNS_ERR ( SERVER_FAILURE ) ;
}
if ( in_packet - > id ! = id ) {
DEBUG ( 0 , ( " DNS packet id mismatch: 0x%0x, expected 0x%0x \n " ,
in_packet - > id , id ) ) ;
return DNS_ERR ( NAME_ERROR ) ;
}
* ancount = in_packet - > ancount ;
* answers = talloc_move ( mem_ctx , & in_packet - > answers ) ;
* nscount = in_packet - > nscount ;
* nsrecs = talloc_move ( mem_ctx , & in_packet - > nsrecs ) ;
* arcount = in_packet - > arcount ;
* additional = talloc_move ( mem_ctx , & in_packet - > additional ) ;
return werr ;
}
2010-10-12 01:39:44 +04:00
static WERROR handle_question ( struct dns_server * dns ,
TALLOC_CTX * mem_ctx ,
const struct dns_name_question * question ,
struct dns_res_rec * * answers , uint16_t * ancount )
2010-10-01 23:59:22 +04:00
{
struct dns_res_rec * ans ;
2010-10-12 01:39:44 +04:00
WERROR werror ;
2010-10-23 17:52:34 +04:00
unsigned int ri ;
2010-10-01 23:59:22 +04:00
struct dnsp_DnssrvRpcRecord * recs ;
2011-12-16 12:43:47 +04:00
uint16_t rec_count , ai = 0 ;
struct ldb_dn * dn = NULL ;
2010-10-01 23:59:22 +04:00
2010-10-12 01:39:44 +04:00
werror = dns_name2dn ( dns , mem_ctx , question - > name , & dn ) ;
W_ERROR_NOT_OK_RETURN ( werror ) ;
2010-10-01 23:59:22 +04:00
2011-12-16 12:43:47 +04:00
werror = dns_lookup_records ( dns , mem_ctx , dn , & recs , & rec_count ) ;
W_ERROR_NOT_OK_RETURN ( werror ) ;
2010-10-01 23:59:22 +04:00
2011-12-16 12:43:47 +04:00
ans = talloc_zero_array ( mem_ctx , struct dns_res_rec , rec_count ) ;
2010-10-12 01:39:44 +04:00
W_ERROR_HAVE_NO_MEMORY ( ans ) ;
2010-10-01 23:59:22 +04:00
2011-12-16 12:43:47 +04:00
for ( ri = 0 ; ri < rec_count ; ri + + ) {
2011-09-28 04:36:42 +04:00
if ( ( question - > question_type ! = DNS_QTYPE_ALL ) & &
( recs [ ri ] . wType ! = question - > question_type ) ) {
continue ;
2010-10-01 23:59:22 +04:00
}
2012-03-09 13:43:27 +04:00
werror = create_response_rr ( question , & recs [ ri ] , & ans , & ai ) ;
W_ERROR_NOT_OK_RETURN ( werror ) ;
2010-10-01 23:59:22 +04:00
}
2011-12-16 12:43:47 +04:00
if ( ai = = 0 ) {
2010-10-12 01:39:44 +04:00
return DNS_ERR ( NAME_ERROR ) ;
2010-10-01 23:59:22 +04:00
}
* ancount = ai ;
* answers = ans ;
2010-10-12 01:39:44 +04:00
return WERR_OK ;
2010-10-01 23:59:22 +04:00
}
2010-10-12 01:39:44 +04:00
WERROR dns_server_process_query ( struct dns_server * dns ,
TALLOC_CTX * mem_ctx ,
struct dns_name_packet * in ,
struct dns_res_rec * * answers , uint16_t * ancount ,
struct dns_res_rec * * nsrecs , uint16_t * nscount ,
struct dns_res_rec * * additional , uint16_t * arcount )
2010-10-01 23:59:22 +04:00
{
2012-03-27 10:42:22 +04:00
uint16_t num_answers = 0 , num_nsrecs = 0 , num_additional = 0 ;
struct dns_res_rec * ans = NULL , * ns = NULL , * adds = NULL ;
2010-10-12 01:39:44 +04:00
WERROR werror ;
2010-10-01 23:59:22 +04:00
2011-09-27 09:31:46 +04:00
if ( in - > qdcount ! = 1 ) {
return DNS_ERR ( FORMAT_ERROR ) ;
}
2011-09-28 05:05:38 +04:00
/* Windows returns NOT_IMPLEMENTED on this as well */
if ( in - > questions [ 0 ] . question_class = = DNS_QCLASS_NONE ) {
return DNS_ERR ( NOT_IMPLEMENTED ) ;
}
2011-09-27 09:31:46 +04:00
werror = handle_question ( dns , mem_ctx , & in - > questions [ 0 ] , & ans , & num_answers ) ;
2012-03-27 10:42:22 +04:00
if ( W_ERROR_EQUAL ( DNS_ERR ( NAME_ERROR ) , werror ) ) {
DEBUG ( 2 , ( " I don't feel responsible for '%s', forwarding \n " , in - > questions [ 0 ] . name ) ) ;
werror = ask_forwarder ( mem_ctx , & in - > questions [ 0 ] , & ans , & num_answers ,
& ns , & num_nsrecs , & adds , & num_additional ) ;
}
2011-09-27 09:31:46 +04:00
W_ERROR_NOT_OK_GOTO ( werror , query_failed ) ;
2010-10-01 23:59:22 +04:00
* answers = ans ;
* ancount = num_answers ;
/*FIXME: Do something for these */
2012-03-27 10:42:22 +04:00
* nsrecs = ns ;
* nscount = num_nsrecs ;
2010-10-01 23:59:22 +04:00
2012-03-27 10:42:22 +04:00
* additional = adds ;
* arcount = num_additional ;
2010-10-01 23:59:22 +04:00
2010-10-12 01:39:44 +04:00
return WERR_OK ;
2011-09-27 09:31:46 +04:00
query_failed :
/*FIXME: add our SOA record to nsrecs */
return werror ;
2010-10-01 23:59:22 +04:00
}