2011-09-27 10:53:45 +04:00
/*
Unix SMB / CIFS implementation .
DNS Server
Copyright ( C ) Amitay Isaacs 2011
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 "dnsserver.h"
# include "lib/util/dlinklist.h"
# include "librpc/gen_ndr/ndr_dnsp.h"
struct dnsserver_zone * dnsserver_db_enumerate_zones ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
bool is_forest )
{
TALLOC_CTX * tmp_ctx ;
const char * domain_prefix = " DC=DomainDnsZones " ;
const char * forest_prefix = " DC=ForestDnsZones " ;
const char * prefix ;
const char * const attrs [ ] = { " name " , NULL } ;
struct ldb_dn * dn_base , * partition_dn , * dn ;
struct ldb_result * res ;
struct dnsserver_zone * zones , * z ;
int i , ret ;
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return NULL ;
}
if ( is_forest ) {
dn_base = ldb_get_root_basedn ( samdb ) ;
} else {
dn_base = ldb_get_default_basedn ( samdb ) ;
}
partition_dn = ldb_dn_copy ( tmp_ctx , dn_base ) ;
if ( partition_dn = = NULL ) {
goto failed ;
}
if ( is_forest ) {
prefix = forest_prefix ;
} else {
prefix = domain_prefix ;
}
if ( ! ldb_dn_add_child_fmt ( partition_dn , " %s " , prefix ) ) {
goto failed ;
}
dn = ldb_dn_copy ( tmp_ctx , partition_dn ) ;
if ( dn = = NULL ) {
goto failed ;
}
if ( ! ldb_dn_add_child_fmt ( dn , " CN=MicrosoftDNS " ) ) {
goto failed ;
}
ret = ldb_search ( samdb , tmp_ctx , & res , dn , LDB_SCOPE_SUBTREE ,
attrs , " (&(objectClass=dnsZone)(!(name=RootDNSServers))) " ) ;
if ( ret ! = LDB_SUCCESS ) {
DEBUG ( 0 , ( " dnsserver: Failed to find DNS Zones in %s \n " ,
ldb_dn_get_linearized ( dn ) ) ) ;
goto failed ;
}
zones = NULL ;
for ( i = 0 ; i < res - > count ; i + + ) {
z = talloc_zero ( mem_ctx , struct dnsserver_zone ) ;
if ( z = = NULL ) {
goto failed ;
}
z - > name = talloc_strdup ( z ,
ldb_msg_find_attr_as_string ( res - > msgs [ i ] , " name " , NULL ) ) ;
DEBUG ( 2 , ( " dnsserver: Found DNS zone %s \n " , z - > name ) ) ;
z - > zone_dn = talloc_steal ( z , res - > msgs [ i ] - > dn ) ;
z - > partition_dn = talloc_steal ( z , partition_dn ) ;
DLIST_ADD_END ( zones , z , NULL ) ;
}
return zones ;
failed :
talloc_free ( tmp_ctx ) ;
return NULL ;
}
2011-10-18 08:20:14 +04:00
static unsigned int dnsserver_update_soa ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
struct dnsserver_zone * z )
{
const char * const attrs [ ] = { " dnsRecord " , NULL } ;
struct ldb_result * res ;
struct dnsp_DnssrvRpcRecord rec ;
struct ldb_message_element * el ;
enum ndr_err_code ndr_err ;
int ret , i , serial = - 1 ;
NTTIME t ;
unix_to_nt_time ( & t , time ( NULL ) ) ;
t / = 10 * 1000 * 1000 ; /* convert to seconds (NT time is in 100ns units) */
t / = 3600 ; /* convert to hours */
ret = ldb_search ( samdb , mem_ctx , & res , z - > zone_dn , LDB_SCOPE_ONELEVEL , attrs ,
" (&(objectClass=dnsNode)(name=@)) " ) ;
if ( ret ! = LDB_SUCCESS | | res - > count = = 0 ) {
return - 1 ;
}
el = ldb_msg_find_element ( res - > msgs [ 0 ] , " dnsRecord " ) ;
if ( el = = NULL ) {
return - 1 ;
}
for ( i = 0 ; i < el - > num_values ; i + + ) {
ndr_err = ndr_pull_struct_blob ( & el - > values [ i ] , mem_ctx , & rec ,
( ndr_pull_flags_fn_t ) ndr_pull_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
continue ;
}
if ( rec . wType = = DNS_TYPE_SOA ) {
serial = rec . data . soa . serial + 1 ;
rec . dwSerial = serial ;
rec . dwTimeStamp = ( uint32_t ) t ;
rec . data . soa . serial = serial ;
ndr_err = ndr_push_struct_blob ( & el - > values [ i ] , mem_ctx , & rec ,
( ndr_push_flags_fn_t ) ndr_push_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return - 1 ;
}
break ;
}
}
if ( serial ! = - 1 ) {
el - > flags = LDB_FLAG_MOD_REPLACE ;
ret = ldb_modify ( samdb , res - > msgs [ 0 ] ) ;
if ( ret ! = LDB_SUCCESS ) {
return - 1 ;
}
}
return serial ;
}
2011-09-27 10:53:45 +04:00
static WERROR dnsserver_db_do_add_rec ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
struct ldb_dn * dn ,
struct dnsp_DnssrvRpcRecord * rec )
{
struct ldb_message * msg ;
struct ldb_val v ;
int ret ;
enum ndr_err_code ndr_err ;
msg = ldb_msg_new ( mem_ctx ) ;
W_ERROR_HAVE_NO_MEMORY ( msg ) ;
msg - > dn = dn ;
ret = ldb_msg_add_string ( msg , " objectClass " , " dnsNode " ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_NOMEM ;
}
if ( rec ) {
ndr_err = ndr_push_struct_blob ( & v , mem_ctx , rec ,
( ndr_push_flags_fn_t ) ndr_push_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
ret = ldb_msg_add_value ( msg , " dnsRecord " , & v , NULL ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_NOMEM ;
}
}
ret = ldb_add ( samdb , msg ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
return WERR_OK ;
}
WERROR dnsserver_db_add_empty_node ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
struct dnsserver_zone * z ,
const char * name )
{
const char * const attrs [ ] = { " name " , NULL } ;
struct ldb_result * res ;
struct ldb_dn * dn ;
int ret ;
ret = ldb_search ( samdb , mem_ctx , & res , z - > zone_dn , LDB_SCOPE_BASE , attrs ,
" (&(objectClass=dnsNode)(name=%s)) " , name ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
if ( res - > count > 0 ) {
talloc_free ( res ) ;
return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS ;
}
dn = ldb_dn_copy ( mem_ctx , z - > zone_dn ) ;
W_ERROR_HAVE_NO_MEMORY ( dn ) ;
if ( ! ldb_dn_add_child_fmt ( dn , " DC=%s " , name ) ) {
return WERR_NOMEM ;
}
return dnsserver_db_do_add_rec ( mem_ctx , samdb , dn , NULL ) ;
}
WERROR dnsserver_db_add_record ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
struct dnsserver_zone * z ,
const char * name ,
struct DNS_RPC_RECORD * add_record )
{
const char * const attrs [ ] = { " dnsRecord " , NULL } ;
struct ldb_result * res ;
struct dnsp_DnssrvRpcRecord * rec ;
struct ldb_message_element * el ;
struct ldb_dn * dn ;
enum ndr_err_code ndr_err ;
NTTIME t ;
int ret , i ;
2011-10-18 08:20:14 +04:00
int serial ;
2011-09-27 10:53:45 +04:00
rec = dns_to_dnsp_copy ( mem_ctx , add_record ) ;
W_ERROR_HAVE_NO_MEMORY ( rec ) ;
2011-10-18 08:20:14 +04:00
serial = dnsserver_update_soa ( mem_ctx , samdb , z ) ;
if ( serial < 0 ) {
return WERR_INTERNAL_DB_ERROR ;
}
2011-09-27 10:53:45 +04:00
unix_to_nt_time ( & t , time ( NULL ) ) ;
2011-10-18 08:20:14 +04:00
t / = 10 * 1000 * 1000 ; /* convert to seconds (NT time is in 100ns units) */
t / = 3600 ; /* convert to hours */
2011-09-27 10:53:45 +04:00
2011-10-18 08:20:14 +04:00
rec - > dwSerial = serial ;
2011-09-27 10:53:45 +04:00
rec - > dwTimeStamp = t ;
ret = ldb_search ( samdb , mem_ctx , & res , z - > zone_dn , LDB_SCOPE_ONELEVEL , attrs ,
" (&(objectClass=dnsNode)(name=%s)) " , name ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
if ( res - > count = = 0 ) {
dn = dnsserver_name_to_dn ( mem_ctx , z , name ) ;
W_ERROR_HAVE_NO_MEMORY ( dn ) ;
return dnsserver_db_do_add_rec ( mem_ctx , samdb , dn , rec ) ;
}
el = ldb_msg_find_element ( res - > msgs [ 0 ] , " dnsRecord " ) ;
if ( el = = NULL ) {
ret = ldb_msg_add_empty ( res - > msgs [ 0 ] , " dnsRecord " , 0 , & el ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_NOMEM ;
}
}
for ( i = 0 ; i < el - > num_values ; i + + ) {
struct dnsp_DnssrvRpcRecord rec2 ;
ndr_err = ndr_pull_struct_blob ( & el - > values [ i ] , mem_ctx , & rec2 ,
( ndr_pull_flags_fn_t ) ndr_pull_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
if ( dns_record_match ( rec , & rec2 ) ) {
break ;
}
}
if ( i < el - > num_values ) {
return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS ;
}
if ( i = = el - > num_values ) {
/* adding a new value */
el - > values = talloc_realloc ( el , el - > values , struct ldb_val , el - > num_values + 1 ) ;
W_ERROR_HAVE_NO_MEMORY ( el - > values ) ;
el - > num_values + + ;
}
ndr_err = ndr_push_struct_blob ( & el - > values [ i ] , mem_ctx , rec ,
( ndr_push_flags_fn_t ) ndr_push_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
el - > flags = LDB_FLAG_MOD_REPLACE ;
ret = ldb_modify ( samdb , res - > msgs [ 0 ] ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
return WERR_OK ;
}
WERROR dnsserver_db_update_record ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
struct dnsserver_zone * z ,
const char * name ,
struct DNS_RPC_RECORD * add_record ,
struct DNS_RPC_RECORD * del_record )
{
const char * const attrs [ ] = { " dnsRecord " , NULL } ;
struct ldb_result * res ;
struct dnsp_DnssrvRpcRecord * arec , * drec ;
struct ldb_message_element * el ;
enum ndr_err_code ndr_err ;
NTTIME t ;
int ret , i ;
2011-10-18 08:20:14 +04:00
int serial ;
serial = dnsserver_update_soa ( mem_ctx , samdb , z ) ;
if ( serial < 0 ) {
return WERR_INTERNAL_DB_ERROR ;
}
2011-09-27 10:53:45 +04:00
arec = dns_to_dnsp_copy ( mem_ctx , add_record ) ;
W_ERROR_HAVE_NO_MEMORY ( arec ) ;
drec = dns_to_dnsp_copy ( mem_ctx , del_record ) ;
W_ERROR_HAVE_NO_MEMORY ( drec ) ;
unix_to_nt_time ( & t , time ( NULL ) ) ;
t / = 10 * 1000 * 1000 ;
2011-10-18 08:20:14 +04:00
arec - > dwSerial = serial ;
2011-09-27 10:53:45 +04:00
arec - > dwTimeStamp = t ;
ret = ldb_search ( samdb , mem_ctx , & res , z - > zone_dn , LDB_SCOPE_ONELEVEL , attrs ,
" (&(objectClass=dnsNode)(name=%s)) " , name ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
if ( res - > count = = 0 ) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST ;
}
el = ldb_msg_find_element ( res - > msgs [ 0 ] , " dnsRecord " ) ;
if ( el = = NULL | | el - > num_values = = 0 ) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST ;
}
for ( i = 0 ; i < el - > num_values ; i + + ) {
struct dnsp_DnssrvRpcRecord rec2 ;
ndr_err = ndr_pull_struct_blob ( & el - > values [ i ] , mem_ctx , & rec2 ,
( ndr_pull_flags_fn_t ) ndr_pull_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
if ( dns_record_match ( arec , & rec2 ) ) {
break ;
}
}
if ( i < el - > num_values ) {
return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS ;
}
for ( i = 0 ; i < el - > num_values ; i + + ) {
struct dnsp_DnssrvRpcRecord rec2 ;
ndr_err = ndr_pull_struct_blob ( & el - > values [ i ] , mem_ctx , & rec2 ,
( ndr_pull_flags_fn_t ) ndr_pull_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
if ( dns_record_match ( drec , & rec2 ) ) {
break ;
}
}
if ( i = = el - > num_values ) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST ;
}
ndr_err = ndr_push_struct_blob ( & el - > values [ i ] , mem_ctx , arec ,
( ndr_push_flags_fn_t ) ndr_push_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
el - > flags = LDB_FLAG_MOD_REPLACE ;
ret = ldb_modify ( samdb , res - > msgs [ 0 ] ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
return WERR_OK ;
}
WERROR dnsserver_db_delete_record ( TALLOC_CTX * mem_ctx ,
struct ldb_context * samdb ,
struct dnsserver_zone * z ,
const char * name ,
struct DNS_RPC_RECORD * del_record )
{
const char * const attrs [ ] = { " dnsRecord " , NULL } ;
struct ldb_result * res ;
struct dnsp_DnssrvRpcRecord * rec ;
struct ldb_message_element * el ;
enum ndr_err_code ndr_err ;
int ret , i ;
2011-10-18 08:20:14 +04:00
int serial ;
serial = dnsserver_update_soa ( mem_ctx , samdb , z ) ;
if ( serial < 0 ) {
return WERR_INTERNAL_DB_ERROR ;
}
2011-09-27 10:53:45 +04:00
rec = dns_to_dnsp_copy ( mem_ctx , del_record ) ;
W_ERROR_HAVE_NO_MEMORY ( rec ) ;
ret = ldb_search ( samdb , mem_ctx , & res , z - > zone_dn , LDB_SCOPE_ONELEVEL , attrs ,
" (&(objectClass=dnsNode)(name=%s)) " , name ) ;
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
if ( res - > count = = 0 ) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST ;
}
el = ldb_msg_find_element ( res - > msgs [ 0 ] , " dnsRecord " ) ;
if ( el = = NULL | | el - > num_values = = 0 ) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST ;
}
for ( i = 0 ; i < el - > num_values ; i + + ) {
struct dnsp_DnssrvRpcRecord rec2 ;
ndr_err = ndr_pull_struct_blob ( & el - > values [ i ] , mem_ctx , & rec2 ,
( ndr_pull_flags_fn_t ) ndr_pull_dnsp_DnssrvRpcRecord ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return WERR_GENERAL_FAILURE ;
}
if ( dns_record_match ( rec , & rec2 ) ) {
break ;
}
}
if ( i = = el - > num_values ) {
return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST ;
}
if ( i < el - > num_values - 1 ) {
memmove ( & el - > values [ i ] , & el - > values [ i + 1 ] , sizeof ( el - > values [ 0 ] ) * ( ( el - > num_values - 1 ) - i ) ) ;
}
el - > num_values - - ;
if ( el - > num_values = = 0 ) {
ret = ldb_delete ( samdb , res - > msgs [ 0 ] - > dn ) ;
} else {
el - > flags = LDB_FLAG_MOD_REPLACE ;
ret = ldb_modify ( samdb , res - > msgs [ 0 ] ) ;
}
if ( ret ! = LDB_SUCCESS ) {
return WERR_INTERNAL_DB_ERROR ;
}
return WERR_OK ;
}