diff options
Diffstat (limited to 'source4/rpc_server/dnsserver')
-rw-r--r-- | source4/rpc_server/dnsserver/dcerpc_dnsserver.c | 2431 | ||||
-rw-r--r-- | source4/rpc_server/dnsserver/dnsdata.c | 1121 | ||||
-rw-r--r-- | source4/rpc_server/dnsserver/dnsdb.c | 1272 | ||||
-rw-r--r-- | source4/rpc_server/dnsserver/dnsserver.h | 264 | ||||
-rw-r--r-- | source4/rpc_server/dnsserver/dnsutils.c | 414 |
5 files changed, 5502 insertions, 0 deletions
diff --git a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c new file mode 100644 index 0000000..ea4d86d --- /dev/null +++ b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c @@ -0,0 +1,2431 @@ +/* + 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 "talloc.h" +#include "rpc_server/dcerpc_server.h" +#include "rpc_server/common/common.h" +#include "dsdb/samdb/samdb.h" +#include "lib/util/dlinklist.h" +#include "librpc/gen_ndr/ndr_dnsserver.h" +#include "dns_server/dnsserver_common.h" +#include "dnsserver.h" + +#undef strcasecmp + +#define DCESRV_INTERFACE_DNSSERVER_BIND(context, iface) \ + dcesrv_interface_dnsserver_bind(context, iface) +static NTSTATUS dcesrv_interface_dnsserver_bind(struct dcesrv_connection_context *context, + const struct dcesrv_interface *iface) +{ + return dcesrv_interface_bind_require_integrity(context, iface); +} + +#define DNSSERVER_STATE_MAGIC 0xc9657ab4 +struct dnsserver_state { + struct loadparm_context *lp_ctx; + struct ldb_context *samdb; + struct dnsserver_partition *partitions; + struct dnsserver_zone *zones; + int zones_count; + struct dnsserver_serverinfo *serverinfo; +}; + + +/* Utility functions */ + +static void dnsserver_reload_zones(struct dnsserver_state *dsstate) +{ + struct dnsserver_partition *p; + struct dnsserver_zone *zones, *z, *znext, *zmatch; + struct dnsserver_zone *old_list, *new_list; + + old_list = dsstate->zones; + new_list = NULL; + + for (p = dsstate->partitions; p; p = p->next) { + zones = dnsserver_db_enumerate_zones(dsstate, dsstate->samdb, p); + if (zones == NULL) { + continue; + } + for (z = zones; z; ) { + znext = z->next; + zmatch = dnsserver_find_zone(old_list, z->name); + if (zmatch == NULL) { + /* Missing zone */ + z->zoneinfo = dnsserver_init_zoneinfo(z, dsstate->serverinfo); + if (z->zoneinfo == NULL) { + continue; + } + DLIST_ADD_END(new_list, z); + p->zones_count++; + dsstate->zones_count++; + } else { + /* Existing zone */ + talloc_free(z); + DLIST_REMOVE(old_list, zmatch); + DLIST_ADD_END(new_list, zmatch); + } + z = znext; + } + } + + if (new_list == NULL) { + return; + } + + /* Deleted zones */ + for (z = old_list; z; ) { + znext = z->next; + z->partition->zones_count--; + dsstate->zones_count--; + talloc_free(z); + z = znext; + } + + dsstate->zones = new_list; +} + + +static struct dnsserver_state *dnsserver_connect(struct dcesrv_call_state *dce_call) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *zones, *z, *znext; + struct dnsserver_partition *partitions, *p; + NTSTATUS status; + + dsstate = dcesrv_iface_state_find_conn(dce_call, + DNSSERVER_STATE_MAGIC, + struct dnsserver_state); + if (dsstate != NULL) { + return dsstate; + } + + dsstate = talloc_zero(dce_call, struct dnsserver_state); + if (dsstate == NULL) { + return NULL; + } + + dsstate->lp_ctx = dce_call->conn->dce_ctx->lp_ctx; + + dsstate->samdb = dcesrv_samdb_connect_as_user(dsstate, dce_call); + if (dsstate->samdb == NULL) { + DEBUG(0,("dnsserver: Failed to open samdb")); + goto failed; + } + + /* Initialize server info */ + dsstate->serverinfo = dnsserver_init_serverinfo(dsstate, + dsstate->lp_ctx, + dsstate->samdb); + if (dsstate->serverinfo == NULL) { + goto failed; + } + + /* Search for DNS partitions */ + partitions = dnsserver_db_enumerate_partitions(dsstate, dsstate->serverinfo, dsstate->samdb); + if (partitions == NULL) { + goto failed; + } + dsstate->partitions = partitions; + + /* Search for DNS zones */ + for (p = partitions; p; p = p->next) { + zones = dnsserver_db_enumerate_zones(dsstate, dsstate->samdb, p); + if (zones == NULL) { + goto failed; + } + for (z = zones; z; ) { + znext = z->next; + if (dnsserver_find_zone(dsstate->zones, z->name) == NULL) { + z->zoneinfo = dnsserver_init_zoneinfo(z, dsstate->serverinfo); + if (z->zoneinfo == NULL) { + goto failed; + } + DLIST_ADD_END(dsstate->zones, z); + p->zones_count++; + dsstate->zones_count++; + } else { + /* Ignore duplicate zone */ + DEBUG(3,("dnsserver: Ignoring duplicate zone '%s' from '%s'", + z->name, ldb_dn_get_linearized(z->zone_dn))); + } + z = znext; + } + } + + status = dcesrv_iface_state_store_conn(dce_call, + DNSSERVER_STATE_MAGIC, + dsstate); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + return dsstate; + +failed: + talloc_free(dsstate); + dsstate = NULL; + return NULL; +} + + +/* dnsserver query functions */ + +/* [MS-DNSP].pdf Section 3.1.1.1 DNS Server Configuration Information */ +static WERROR dnsserver_query_server(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + const char *operation, + const unsigned int client_version, + enum DNS_RPC_TYPEID *typeid, + union DNSSRV_RPC_UNION *r) +{ + uint8_t is_integer, is_addresses, is_string, is_wstring, is_stringlist; + uint32_t answer_integer; + struct IP4_ARRAY *answer_iparray; + struct DNS_ADDR_ARRAY *answer_addrarray; + char *answer_string; + struct DNS_RPC_UTF8_STRING_LIST *answer_stringlist; + struct dnsserver_serverinfo *serverinfo; + + serverinfo = dsstate->serverinfo; + + if (strcasecmp(operation, "ServerInfo") == 0) { + if (client_version == DNS_CLIENT_VERSION_W2K) { + *typeid = DNSSRV_TYPEID_SERVER_INFO_W2K; + r->ServerInfoW2K = talloc_zero(mem_ctx, struct DNS_RPC_SERVER_INFO_W2K); + + r->ServerInfoW2K->dwVersion = serverinfo->dwVersion; + r->ServerInfoW2K->fBootMethod = serverinfo->fBootMethod; + r->ServerInfoW2K->fAdminConfigured = serverinfo->fAdminConfigured; + r->ServerInfoW2K->fAllowUpdate = serverinfo->fAllowUpdate; + r->ServerInfoW2K->fDsAvailable = serverinfo->fDsAvailable; + r->ServerInfoW2K->pszServerName = talloc_strdup(mem_ctx, serverinfo->pszServerName); + r->ServerInfoW2K->pszDsContainer = talloc_strdup(mem_ctx, serverinfo->pszDsContainer); + r->ServerInfoW2K->aipServerAddrs = dns_addr_array_to_ip4_array(mem_ctx, + serverinfo->aipServerAddrs); + r->ServerInfoW2K->aipListenAddrs = dns_addr_array_to_ip4_array(mem_ctx, + serverinfo->aipListenAddrs); + r->ServerInfoW2K->aipForwarders = ip4_array_copy(mem_ctx, serverinfo->aipForwarders); + r->ServerInfoW2K->dwLogLevel = serverinfo->dwLogLevel; + r->ServerInfoW2K->dwDebugLevel = serverinfo->dwDebugLevel; + r->ServerInfoW2K->dwForwardTimeout = serverinfo->dwForwardTimeout; + r->ServerInfoW2K->dwRpcProtocol = serverinfo->dwRpcProtocol; + r->ServerInfoW2K->dwNameCheckFlag = serverinfo->dwNameCheckFlag; + r->ServerInfoW2K->cAddressAnswerLimit = serverinfo->cAddressAnswerLimit; + r->ServerInfoW2K->dwRecursionRetry = serverinfo->dwRecursionRetry; + r->ServerInfoW2K->dwRecursionTimeout = serverinfo->dwRecursionTimeout; + r->ServerInfoW2K->dwMaxCacheTtl = serverinfo->dwMaxCacheTtl; + r->ServerInfoW2K->dwDsPollingInterval = serverinfo->dwDsPollingInterval; + r->ServerInfoW2K->dwScavengingInterval = serverinfo->dwScavengingInterval; + r->ServerInfoW2K->dwDefaultRefreshInterval = serverinfo->dwDefaultRefreshInterval; + r->ServerInfoW2K->dwDefaultNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval; + r->ServerInfoW2K->fAutoReverseZones = serverinfo->fAutoReverseZones; + r->ServerInfoW2K->fAutoCacheUpdate = serverinfo->fAutoCacheUpdate; + r->ServerInfoW2K->fRecurseAfterForwarding = serverinfo->fRecurseAfterForwarding; + r->ServerInfoW2K->fForwardDelegations = serverinfo->fForwardDelegations; + r->ServerInfoW2K->fNoRecursion = serverinfo->fNoRecursion; + r->ServerInfoW2K->fSecureResponses = serverinfo->fSecureResponses; + r->ServerInfoW2K->fRoundRobin = serverinfo->fRoundRobin; + r->ServerInfoW2K->fLocalNetPriority = serverinfo->fLocalNetPriority; + r->ServerInfoW2K->fBindSecondaries = serverinfo->fBindSecondaries; + r->ServerInfoW2K->fWriteAuthorityNs = serverinfo->fWriteAuthorityNs; + r->ServerInfoW2K->fStrictFileParsing = serverinfo->fStrictFileParsing; + r->ServerInfoW2K->fLooseWildcarding = serverinfo->fLooseWildcarding; + r->ServerInfoW2K->fDefaultAgingState = serverinfo->fDefaultAgingState; + + } else if (client_version == DNS_CLIENT_VERSION_DOTNET) { + *typeid = DNSSRV_TYPEID_SERVER_INFO_DOTNET; + r->ServerInfoDotNet = talloc_zero(mem_ctx, struct DNS_RPC_SERVER_INFO_DOTNET); + + r->ServerInfoDotNet->dwRpcStructureVersion = 0x01; + r->ServerInfoDotNet->dwVersion = serverinfo->dwVersion; + r->ServerInfoDotNet->fBootMethod = serverinfo->fBootMethod; + r->ServerInfoDotNet->fAdminConfigured = serverinfo->fAdminConfigured; + r->ServerInfoDotNet->fAllowUpdate = serverinfo->fAllowUpdate; + r->ServerInfoDotNet->fDsAvailable = serverinfo->fDsAvailable; + r->ServerInfoDotNet->pszServerName = talloc_strdup(mem_ctx, serverinfo->pszServerName); + r->ServerInfoDotNet->pszDsContainer = talloc_strdup(mem_ctx, serverinfo->pszDsContainer); + r->ServerInfoDotNet->aipServerAddrs = dns_addr_array_to_ip4_array(mem_ctx, + serverinfo->aipServerAddrs); + r->ServerInfoDotNet->aipListenAddrs = dns_addr_array_to_ip4_array(mem_ctx, + serverinfo->aipListenAddrs); + r->ServerInfoDotNet->aipForwarders = ip4_array_copy(mem_ctx, serverinfo->aipForwarders); + r->ServerInfoDotNet->aipLogFilter = ip4_array_copy(mem_ctx, serverinfo->aipLogFilter); + r->ServerInfoDotNet->pwszLogFilePath = talloc_strdup(mem_ctx, serverinfo->pwszLogFilePath); + r->ServerInfoDotNet->pszDomainName = talloc_strdup(mem_ctx, serverinfo->pszDomainName); + r->ServerInfoDotNet->pszForestName = talloc_strdup(mem_ctx, serverinfo->pszForestName); + r->ServerInfoDotNet->pszDomainDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszDomainDirectoryPartition); + r->ServerInfoDotNet->pszForestDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszForestDirectoryPartition); + r->ServerInfoDotNet->dwLogLevel = serverinfo->dwLogLevel; + r->ServerInfoDotNet->dwDebugLevel = serverinfo->dwDebugLevel; + r->ServerInfoDotNet->dwForwardTimeout = serverinfo->dwForwardTimeout; + r->ServerInfoDotNet->dwRpcProtocol = serverinfo->dwRpcProtocol; + r->ServerInfoDotNet->dwNameCheckFlag = serverinfo->dwNameCheckFlag; + r->ServerInfoDotNet->cAddressAnswerLimit = serverinfo->cAddressAnswerLimit; + r->ServerInfoDotNet->dwRecursionRetry = serverinfo->dwRecursionRetry; + r->ServerInfoDotNet->dwRecursionTimeout = serverinfo->dwRecursionTimeout; + r->ServerInfoDotNet->dwMaxCacheTtl = serverinfo->dwMaxCacheTtl; + r->ServerInfoDotNet->dwDsPollingInterval = serverinfo->dwDsPollingInterval; + r->ServerInfoDotNet->dwLocalNetPriorityNetMask = serverinfo->dwLocalNetPriorityNetMask; + r->ServerInfoDotNet->dwScavengingInterval = serverinfo->dwScavengingInterval; + r->ServerInfoDotNet->dwDefaultRefreshInterval = serverinfo->dwDefaultRefreshInterval; + r->ServerInfoDotNet->dwDefaultNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval; + r->ServerInfoDotNet->dwLastScavengeTime = serverinfo->dwLastScavengeTime; + r->ServerInfoDotNet->dwEventLogLevel = serverinfo->dwEventLogLevel; + r->ServerInfoDotNet->dwLogFileMaxSize = serverinfo->dwLogFileMaxSize; + r->ServerInfoDotNet->dwDsForestVersion = serverinfo->dwDsForestVersion; + r->ServerInfoDotNet->dwDsDomainVersion = serverinfo->dwDsDomainVersion; + r->ServerInfoDotNet->dwDsDsaVersion = serverinfo->dwDsDsaVersion; + r->ServerInfoDotNet->fAutoReverseZones = serverinfo->fAutoReverseZones; + r->ServerInfoDotNet->fAutoCacheUpdate = serverinfo->fAutoCacheUpdate; + r->ServerInfoDotNet->fRecurseAfterForwarding = serverinfo->fRecurseAfterForwarding; + r->ServerInfoDotNet->fForwardDelegations = serverinfo->fForwardDelegations; + r->ServerInfoDotNet->fNoRecursion = serverinfo->fNoRecursion; + r->ServerInfoDotNet->fSecureResponses = serverinfo->fSecureResponses; + r->ServerInfoDotNet->fRoundRobin = serverinfo->fRoundRobin; + r->ServerInfoDotNet->fLocalNetPriority = serverinfo->fLocalNetPriority; + r->ServerInfoDotNet->fBindSecondaries = serverinfo->fBindSecondaries; + r->ServerInfoDotNet->fWriteAuthorityNs = serverinfo->fWriteAuthorityNs; + r->ServerInfoDotNet->fStrictFileParsing = serverinfo->fStrictFileParsing; + r->ServerInfoDotNet->fLooseWildcarding = serverinfo->fLooseWildcarding; + r->ServerInfoDotNet->fDefaultAgingState = serverinfo->fDefaultAgingState; + + } else if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + *typeid = DNSSRV_TYPEID_SERVER_INFO; + r->ServerInfo = talloc_zero(mem_ctx, struct DNS_RPC_SERVER_INFO_LONGHORN); + + r->ServerInfo->dwRpcStructureVersion = 0x02; + r->ServerInfo->dwVersion = serverinfo->dwVersion; + r->ServerInfo->fBootMethod = serverinfo->fBootMethod; + r->ServerInfo->fAdminConfigured = serverinfo->fAdminConfigured; + r->ServerInfo->fAllowUpdate = serverinfo->fAllowUpdate; + r->ServerInfo->fDsAvailable = serverinfo->fDsAvailable; + r->ServerInfo->pszServerName = talloc_strdup(mem_ctx, serverinfo->pszServerName); + r->ServerInfo->pszDsContainer = talloc_strdup(mem_ctx, serverinfo->pszDsContainer); + r->ServerInfo->aipServerAddrs = serverinfo->aipServerAddrs; + r->ServerInfo->aipListenAddrs = serverinfo->aipListenAddrs; + r->ServerInfo->aipForwarders = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipForwarders); + r->ServerInfo->aipLogFilter = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipLogFilter); + r->ServerInfo->pwszLogFilePath = talloc_strdup(mem_ctx, serverinfo->pwszLogFilePath); + r->ServerInfo->pszDomainName = talloc_strdup(mem_ctx, serverinfo->pszDomainName); + r->ServerInfo->pszForestName = talloc_strdup(mem_ctx, serverinfo->pszForestName); + r->ServerInfo->pszDomainDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszDomainDirectoryPartition); + r->ServerInfo->pszForestDirectoryPartition = talloc_strdup(mem_ctx, serverinfo->pszForestDirectoryPartition); + r->ServerInfo->dwLogLevel = serverinfo->dwLogLevel; + r->ServerInfo->dwDebugLevel = serverinfo->dwDebugLevel; + r->ServerInfo->dwForwardTimeout = serverinfo->dwForwardTimeout; + r->ServerInfo->dwRpcProtocol = serverinfo->dwRpcProtocol; + r->ServerInfo->dwNameCheckFlag = serverinfo->dwNameCheckFlag; + r->ServerInfo->cAddressAnswerLimit = serverinfo->cAddressAnswerLimit; + r->ServerInfo->dwRecursionRetry = serverinfo->dwRecursionRetry; + r->ServerInfo->dwRecursionTimeout = serverinfo->dwRecursionTimeout; + r->ServerInfo->dwMaxCacheTtl = serverinfo->dwMaxCacheTtl; + r->ServerInfo->dwDsPollingInterval = serverinfo->dwDsPollingInterval; + r->ServerInfo->dwLocalNetPriorityNetMask = serverinfo->dwLocalNetPriorityNetMask; + r->ServerInfo->dwScavengingInterval = serverinfo->dwScavengingInterval; + r->ServerInfo->dwDefaultRefreshInterval = serverinfo->dwDefaultRefreshInterval; + r->ServerInfo->dwDefaultNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval; + r->ServerInfo->dwLastScavengeTime = serverinfo->dwLastScavengeTime; + r->ServerInfo->dwEventLogLevel = serverinfo->dwEventLogLevel; + r->ServerInfo->dwLogFileMaxSize = serverinfo->dwLogFileMaxSize; + r->ServerInfo->dwDsForestVersion = serverinfo->dwDsForestVersion; + r->ServerInfo->dwDsDomainVersion = serverinfo->dwDsDomainVersion; + r->ServerInfo->dwDsDsaVersion = serverinfo->dwDsDsaVersion; + r->ServerInfo->fReadOnlyDC = serverinfo->fReadOnlyDC; + r->ServerInfo->fAutoReverseZones = serverinfo->fAutoReverseZones; + r->ServerInfo->fAutoCacheUpdate = serverinfo->fAutoCacheUpdate; + r->ServerInfo->fRecurseAfterForwarding = serverinfo->fRecurseAfterForwarding; + r->ServerInfo->fForwardDelegations = serverinfo->fForwardDelegations; + r->ServerInfo->fNoRecursion = serverinfo->fNoRecursion; + r->ServerInfo->fSecureResponses = serverinfo->fSecureResponses; + r->ServerInfo->fRoundRobin = serverinfo->fRoundRobin; + r->ServerInfo->fLocalNetPriority = serverinfo->fLocalNetPriority; + r->ServerInfo->fBindSecondaries = serverinfo->fBindSecondaries; + r->ServerInfo->fWriteAuthorityNs = serverinfo->fWriteAuthorityNs; + r->ServerInfo->fStrictFileParsing = serverinfo->fStrictFileParsing; + r->ServerInfo->fLooseWildcarding = serverinfo->fLooseWildcarding; + r->ServerInfo->fDefaultAgingState = serverinfo->fDefaultAgingState; + } + return WERR_OK; + } + + is_integer = 0; + answer_integer = 0; + + if (strcasecmp(operation, "AddressAnswerLimit") == 0) { + answer_integer = serverinfo->cAddressAnswerLimit; + is_integer = 1; + } else if (strcasecmp(operation, "AdminConfigured") == 0) { + answer_integer = serverinfo->fAdminConfigured; + is_integer = 1; + } else if (strcasecmp(operation, "AllowCNAMEAtNS") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "AllowUpdate") == 0) { + answer_integer = serverinfo->fAllowUpdate; + is_integer = 1; + } else if (strcasecmp(operation, "AutoCacheUpdate") == 0) { + answer_integer = serverinfo->fAutoCacheUpdate; + is_integer = 1; + } else if (strcasecmp(operation, "AutoConfigFileZones") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "BindSecondaries") == 0) { + answer_integer = serverinfo->fBindSecondaries; + is_integer = 1; + } else if (strcasecmp(operation, "BootMethod") == 0) { + answer_integer = serverinfo->fBootMethod; + is_integer = 1; + } else if (strcasecmp(operation, "DebugLevel") == 0) { + answer_integer = serverinfo->dwDebugLevel; + is_integer = 1; + } else if (strcasecmp(operation, "DefaultAgingState") == 0) { + answer_integer = serverinfo->fDefaultAgingState; + is_integer = 1; + } else if (strcasecmp(operation, "DefaultNoRefreshInterval") == 0) { + answer_integer = serverinfo->dwDefaultNoRefreshInterval; + is_integer = 1; + } else if (strcasecmp(operation, "DefaultRefreshInterval") == 0) { + answer_integer = serverinfo->dwDefaultRefreshInterval; + is_integer = 1; + } else if (strcasecmp(operation, "DeleteOutsideGlue") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DisjointNets") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DsLazyUpdateInterval") == 0) { + answer_integer = 3; /* seconds */ + is_integer = 1; + } else if (strcasecmp(operation, "DsPollingInterval") == 0) { + answer_integer = serverinfo->dwDsPollingInterval; + is_integer = 1; + } else if (strcasecmp(operation, "DsTombstoneInterval") == 0) { + answer_integer = 0x00127500; /* 14 days */ + is_integer = 1; + } else if (strcasecmp(operation, "EnableRegistryBoot") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EventLogLevel") == 0) { + answer_integer = serverinfo->dwEventLogLevel; + is_integer = 1; + } else if (strcasecmp(operation, "ForceSoaSerial") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ForceSaoRetry") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ForceSoaRefresh") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ForceSoaMinimumTtl") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ForwardDelegations") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "ForwardingTimeout") == 0) { + answer_integer = serverinfo->dwForwardTimeout; + is_integer = 1; + } else if (strcasecmp(operation, "IsSlave") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "LocalNetPriority") == 0) { + answer_integer = serverinfo->fLocalNetPriority; + is_integer = 1; + } else if (strcasecmp(operation, "LogFileMaxSize") == 0) { + answer_integer = serverinfo->dwLogFileMaxSize; + is_integer = 1; + } else if (strcasecmp(operation, "LogLevel") == 0) { + answer_integer = serverinfo->dwLogLevel; + is_integer = 1; + } else if (strcasecmp(operation, "LooseWildcarding") == 0) { + answer_integer = serverinfo->fLooseWildcarding; + is_integer = 1; + } else if (strcasecmp(operation, "MaxCacheTtl") == 0) { + answer_integer = serverinfo->dwMaxCacheTtl; + is_integer = 1; + } else if (strcasecmp(operation, "MaxNegativeCacheTtl") == 0) { + answer_integer = 0x00000384; /* 15 minutes */ + is_integer = 1; + } else if (strcasecmp(operation, "NameCheckFlag") == 0) { + answer_integer = serverinfo->dwNameCheckFlag; + is_integer = 1; + } else if (strcasecmp(operation, "NoRecursion") == 0) { + answer_integer = serverinfo->fNoRecursion; + is_integer = 1; + } else if (strcasecmp(operation, "NoUpdateDelegations") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "PublishAutonet") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "QuietRecvFaultInterval") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "QuietRecvLogInterval") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "RecursionRetry") == 0) { + answer_integer = serverinfo->dwRecursionRetry; + is_integer = 1; + } else if (strcasecmp(operation, "RecursionTimeout") == 0) { + answer_integer = serverinfo->dwRecursionTimeout; + is_integer = 1; + } else if (strcasecmp(operation, "ReloadException") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "RoundRobin") == 0) { + answer_integer = serverinfo->fRoundRobin; + is_integer = 1; + } else if (strcasecmp(operation, "RpcProtocol") == 0) { + answer_integer = serverinfo->dwRpcProtocol; + is_integer = 1; + } else if (strcasecmp(operation, "SecureResponses") == 0) { + answer_integer = serverinfo->fSecureResponses; + is_integer = 1; + } else if (strcasecmp(operation, "SendPort") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ScavengingInterval") == 0) { + answer_integer = serverinfo->dwScavengingInterval; + is_integer = 1; + } else if (strcasecmp(operation, "SocketPoolSize") == 0) { + answer_integer = 0x000009C4; + is_integer = 1; + } else if (strcasecmp(operation, "StrictFileParsing") == 0) { + answer_integer = serverinfo->fStrictFileParsing; + is_integer = 1; + } else if (strcasecmp(operation, "SyncDnsZoneSerial") == 0) { + answer_integer = 2; /* ZONE_SERIAL_SYNC_XFER */ + is_integer = 1; + } else if (strcasecmp(operation, "UpdateOptions") == 0) { + answer_integer = 0x0000030F; /* DNS_DEFAULT_UPDATE_OPTIONS */ + is_integer = 1; + } else if (strcasecmp(operation, "UseSystemEvengLog") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "Version") == 0) { + answer_integer = serverinfo->dwVersion; + is_integer = 1; + } else if (strcasecmp(operation, "XfrConnectTimeout") == 0) { + answer_integer = 0x0000001E; + is_integer = 1; + } else if (strcasecmp(operation, "WriteAuthorityNs") == 0) { + answer_integer = serverinfo->fWriteAuthorityNs; + is_integer = 1; + } else if (strcasecmp(operation, "AdditionalRecursionTimeout") == 0) { + answer_integer = 0x00000004; + is_integer = 1; + } else if (strcasecmp(operation, "AppendMsZoneTransferFlag") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "AutoCreateDelegations") == 0) { + answer_integer = 0; /* DNS_ACD_DONT_CREATE */ + is_integer = 1; + } else if (strcasecmp(operation, "BreakOnAscFailure") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "CacheEmptyAuthResponses") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DirectoryPartitionAutoEnlistInterval") == 0) { + answer_integer = 0x00015180; /* 1 day */ + is_integer = 1; + } else if (strcasecmp(operation, "DisableAutoReverseZones") == 0) { + answer_integer = ~serverinfo->fAutoReverseZones; + is_integer = 1; + } else if (strcasecmp(operation, "EDnsCacheTimeout") == 0) { + answer_integer = 0x00000384; /* 15 minutes */ + is_integer = 1; + } else if (strcasecmp(operation, "EnableDirectoryPartitions") == 0) { + answer_integer = serverinfo->fDsAvailable; + is_integer = 1; + } else if (strcasecmp(operation, "EnableDnsSec") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableEDnsProbes") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableEDnsReception") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableIPv6") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableIQueryResponseGeneration") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableSendErrorSuppression") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableUpdateForwarding") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableWinsR") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ForceDsaBehaviorVersion") == 0) { + answer_integer = serverinfo->dwDsDsaVersion; + is_integer = 1; + } else if (strcasecmp(operation, "ForceDomainBehaviorVersion") == 0) { + answer_integer = serverinfo->dwDsDsaVersion; + is_integer = 1; + } else if (strcasecmp(operation, "ForceForestBehaviorVersion") == 0) { + answer_integer = serverinfo->dwDsDsaVersion; + is_integer = 1; + } else if (strcasecmp(operation, "HeapDebug") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "LameDelegationTtl") == 0) { + answer_integer = 0; /* seconds */ + is_integer = 1; + } else if (strcasecmp(operation, "LocalNetPriorityNetMask") == 0) { + answer_integer = serverinfo->dwLocalNetPriorityNetMask; + is_integer = 1; + } else if (strcasecmp(operation, "MaxCacheSize") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "MaxResourceRecordsInNonSecureUpdate") == 0) { + answer_integer = 0x0000001E; + is_integer = 1; + } else if (strcasecmp(operation, "OperationsLogLevel") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "OperationsLogLevel2") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "MaximumUdpPacketSize") == 0) { + answer_integer = 0x00004000; /* maximum possible */ + is_integer = 1; + } else if (strcasecmp(operation, "RecurseToInternetRootMask") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "SelfTest") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "SilentlyIgnoreCNameUpdateConflicts") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "TcpReceivePacketSize") == 0) { + answer_integer = 0x00010000; + is_integer = 1; + } else if (strcasecmp(operation, "XfrThrottleMultiplier") == 0) { + answer_integer = 0x0000000A; + is_integer = 1; + } else if (strcasecmp(operation, "AllowMsdcsLookupRetry") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "AllowReadOnlyZoneTransfer") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DsBackGroundLoadPaused") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DsMinimumBackgroundLoadThreads") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DsRemoteReplicationDelay") == 0) { + answer_integer = 0x0000001E; /* 30 seconds */ + is_integer = 1; + } else if (strcasecmp(operation, "EnableDuplicateQuerySuppresion") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableGlobalNamesSupport") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "EnableVersionQuery") == 0) { + answer_integer = 1; /* DNS_VERSION_QUERY_FULL */ + is_integer = 1; + } else if (strcasecmp(operation, "EnableRsoForRodc") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "ForceRODCMode") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesAlwaysQuerySrv") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesBlockUpdates") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesEnableEDnsProbes") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesPreferAAAA") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesQueryOrder") == 0) { + answer_integer = 1; + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesSendTimeout") == 0) { + answer_integer = 3; /* seconds */ + is_integer = 1; + } else if (strcasecmp(operation, "GlobalNamesServerQueryInterval") == 0) { + answer_integer = 0x00005460; /* 6 hours */ + is_integer = 1; + } else if (strcasecmp(operation, "RemoteIPv4RankBoost") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "RemoteIPv6RankBoost") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "MaximumRodcRsoAttemptsPerCycle") == 0) { + answer_integer = 0x00000064; + is_integer = 1; + } else if (strcasecmp(operation, "MaximumRodcRsoQueueLength") == 0) { + answer_integer = 0x0000012C; + is_integer = 1; + } else if (strcasecmp(operation, "EnableGlobalQueryBlockList") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "OpenACLOnProxyUpdates") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "CacheLockingPercent") == 0) { + answer_integer = 0x00000064; + is_integer = 1; + } + + if (is_integer == 1) { + *typeid = DNSSRV_TYPEID_DWORD; + r->Dword = answer_integer; + return WERR_OK; + } + + is_addresses = 0; + + if (strcasecmp(operation, "Forwarders") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipForwarders); + } else { + answer_iparray = ip4_array_copy(mem_ctx, serverinfo->aipForwarders); + } + is_addresses = 1; + } else if (strcasecmp(operation, "ListenAddresses") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = serverinfo->aipListenAddrs; + } else { + answer_iparray = dns_addr_array_to_ip4_array(mem_ctx, serverinfo->aipListenAddrs); + } + is_addresses = 1; + } else if (strcasecmp(operation, "BreakOnReceiveFrom") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = NULL; + } else { + answer_iparray = NULL; + } + is_addresses = 1; + } else if (strcasecmp(operation, "BreakOnUpdateFrom") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = NULL; + } else { + answer_iparray = NULL; + } + is_addresses = 1; + } else if (strcasecmp(operation, "LogIPFilterList") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, serverinfo->aipLogFilter); + } else { + answer_iparray = ip4_array_copy(mem_ctx, serverinfo->aipLogFilter); + } + is_addresses = 1; + } + + if (is_addresses == 1) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + *typeid = DNSSRV_TYPEID_ADDRARRAY; + r->AddrArray = answer_addrarray; + } else { + *typeid = DNSSRV_TYPEID_IPARRAY; + r->IpArray = answer_iparray; + } + return WERR_OK; + } + + is_string = is_wstring = 0; + + if (strcasecmp(operation, "DomainDirectoryPartitionBaseName") == 0) { + answer_string = talloc_strdup(mem_ctx, "DomainDnsZones"); + if (! answer_string) { + return WERR_OUTOFMEMORY; + } + is_string = 1; + } else if (strcasecmp(operation, "ForestDirectoryPartitionBaseName") == 0) { + answer_string = talloc_strdup(mem_ctx, "ForestDnsZones"); + if (! answer_string) { + return WERR_OUTOFMEMORY; + } + is_string = 1; + } else if (strcasecmp(operation, "LogFilePath") == 0) { + answer_string = talloc_strdup(mem_ctx, serverinfo->pwszLogFilePath); + is_wstring = 1; + } else if (strcasecmp(operation, "ServerLevelPluginDll") == 0) { + answer_string = NULL; + is_wstring = 1; + } else if (strcasecmp(operation, "DsBackgroundPauseName") == 0) { + answer_string = NULL; + is_string = 1; + } else if (strcasecmp(operation, "DsNotRoundRobinTypes") == 0) { + answer_string = NULL; + is_string = 1; + } + + if (is_string == 1) { + *typeid = DNSSRV_TYPEID_LPSTR; + r->String = answer_string; + return WERR_OK; + } else if (is_wstring == 1) { + *typeid = DNSSRV_TYPEID_LPWSTR; + r->WideString = answer_string; + return WERR_OK; + } + + is_stringlist = 0; + + if (strcasecmp(operation, "GlobalQueryBlockList") == 0) { + answer_stringlist = NULL; + is_stringlist = 1; + } else if (strcasecmp(operation, "SocketPoolExcludedPortRanges") == 0) { + answer_stringlist = NULL; + is_stringlist = 1; + } + + if (is_stringlist == 1) { + *typeid = DNSSRV_TYPEID_UTF8_STRING_LIST; + r->Utf8StringList = answer_stringlist; + return WERR_OK; + } + + DEBUG(0,("dnsserver: Invalid server operation %s", operation)); + return WERR_DNS_ERROR_INVALID_PROPERTY; +} + +/* [MS-DNSP].pdf Section 3.1.1.2 Zone Configuration Information */ +static WERROR dnsserver_query_zone(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + struct dnsserver_zone *z, + const char *operation, + const unsigned int client_version, + enum DNS_RPC_TYPEID *typeid, + union DNSSRV_RPC_UNION *r) +{ + uint8_t is_integer, is_addresses, is_string; + uint32_t answer_integer = 0; + struct IP4_ARRAY *answer_iparray; + struct DNS_ADDR_ARRAY *answer_addrarray; + char *answer_string; + struct dnsserver_zoneinfo *zoneinfo; + + zoneinfo = z->zoneinfo; + + if (strcasecmp(operation, "Zone") == 0) { + if (client_version == DNS_CLIENT_VERSION_W2K) { + *typeid = DNSSRV_TYPEID_ZONE_W2K; + r->ZoneW2K = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_W2K); + + r->ZoneW2K->pszZoneName = talloc_strdup(mem_ctx, z->name); + r->ZoneW2K->Flags = zoneinfo->Flags; + r->ZoneW2K->ZoneType = zoneinfo->dwZoneType; + r->ZoneW2K->Version = zoneinfo->Version; + } else { + *typeid = DNSSRV_TYPEID_ZONE; + r->Zone = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_DOTNET); + + r->Zone->dwRpcStructureVersion = 0x01; + r->Zone->pszZoneName = talloc_strdup(mem_ctx, z->name); + r->Zone->Flags = zoneinfo->Flags; + r->Zone->ZoneType = zoneinfo->dwZoneType; + r->Zone->Version = zoneinfo->Version; + r->Zone->dwDpFlags = z->partition->dwDpFlags; + r->Zone->pszDpFqdn = talloc_strdup(mem_ctx, z->partition->pszDpFqdn); + } + return WERR_OK; + } + + if (strcasecmp(operation, "ZoneInfo") == 0) { + if (client_version == DNS_CLIENT_VERSION_W2K) { + *typeid = DNSSRV_TYPEID_ZONE_INFO_W2K; + r->ZoneInfoW2K = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_INFO_W2K); + + r->ZoneInfoW2K->pszZoneName = talloc_strdup(mem_ctx, z->name); + r->ZoneInfoW2K->dwZoneType = zoneinfo->dwZoneType; + r->ZoneInfoW2K->fReverse = zoneinfo->fReverse; + r->ZoneInfoW2K->fAllowUpdate = zoneinfo->fAllowUpdate; + r->ZoneInfoW2K->fPaused = zoneinfo->fPaused; + r->ZoneInfoW2K->fShutdown = zoneinfo->fShutdown; + r->ZoneInfoW2K->fAutoCreated = zoneinfo->fAutoCreated; + r->ZoneInfoW2K->fUseDatabase = zoneinfo->fUseDatabase; + r->ZoneInfoW2K->pszDataFile = talloc_strdup(mem_ctx, zoneinfo->pszDataFile); + r->ZoneInfoW2K->aipMasters = ip4_array_copy(mem_ctx, zoneinfo->aipMasters); + r->ZoneInfoW2K->fSecureSecondaries = zoneinfo->fSecureSecondaries; + r->ZoneInfoW2K->fNotifyLevel = zoneinfo->fNotifyLevel; + r->ZoneInfoW2K->aipSecondaries = ip4_array_copy(mem_ctx, zoneinfo->aipSecondaries); + r->ZoneInfoW2K->aipNotify = ip4_array_copy(mem_ctx, zoneinfo->aipNotify); + r->ZoneInfoW2K->fUseWins = zoneinfo->fUseWins; + r->ZoneInfoW2K->fUseNbstat = zoneinfo->fUseNbstat; + r->ZoneInfoW2K->fAging = zoneinfo->fAging; + r->ZoneInfoW2K->dwNoRefreshInterval = zoneinfo->dwNoRefreshInterval; + r->ZoneInfoW2K->dwRefreshInterval = zoneinfo->dwRefreshInterval; + r->ZoneInfoW2K->dwAvailForScavengeTime = zoneinfo->dwAvailForScavengeTime; + r->ZoneInfoW2K->aipScavengeServers = ip4_array_copy(mem_ctx, zoneinfo->aipScavengeServers); + + } else if (client_version == DNS_CLIENT_VERSION_DOTNET) { + *typeid = DNSSRV_TYPEID_ZONE_INFO_DOTNET; + r->ZoneInfoDotNet = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_INFO_DOTNET); + + r->ZoneInfoDotNet->dwRpcStructureVersion = 0x01; + r->ZoneInfoDotNet->pszZoneName = talloc_strdup(mem_ctx, z->name); + r->ZoneInfoDotNet->dwZoneType = zoneinfo->dwZoneType; + r->ZoneInfoDotNet->fReverse = zoneinfo->fReverse; + r->ZoneInfoDotNet->fAllowUpdate = zoneinfo->fAllowUpdate; + r->ZoneInfoDotNet->fPaused = zoneinfo->fPaused; + r->ZoneInfoDotNet->fShutdown = zoneinfo->fShutdown; + r->ZoneInfoDotNet->fAutoCreated = zoneinfo->fAutoCreated; + r->ZoneInfoDotNet->fUseDatabase = zoneinfo->fUseDatabase; + r->ZoneInfoDotNet->pszDataFile = talloc_strdup(mem_ctx, zoneinfo->pszDataFile); + r->ZoneInfoDotNet->aipMasters = ip4_array_copy(mem_ctx, zoneinfo->aipMasters); + r->ZoneInfoDotNet->fSecureSecondaries = zoneinfo->fSecureSecondaries; + r->ZoneInfoDotNet->fNotifyLevel = zoneinfo->fNotifyLevel; + r->ZoneInfoDotNet->aipSecondaries = ip4_array_copy(mem_ctx, zoneinfo->aipSecondaries); + r->ZoneInfoDotNet->aipNotify = ip4_array_copy(mem_ctx, zoneinfo->aipNotify); + r->ZoneInfoDotNet->fUseWins = zoneinfo->fUseWins; + r->ZoneInfoDotNet->fUseNbstat = zoneinfo->fUseNbstat; + r->ZoneInfoDotNet->fAging = zoneinfo->fAging; + r->ZoneInfoDotNet->dwNoRefreshInterval = zoneinfo->dwNoRefreshInterval; + r->ZoneInfoDotNet->dwRefreshInterval = zoneinfo->dwRefreshInterval; + r->ZoneInfoDotNet->dwAvailForScavengeTime = zoneinfo->dwAvailForScavengeTime; + r->ZoneInfoDotNet->aipScavengeServers = ip4_array_copy(mem_ctx, zoneinfo->aipScavengeServers); + r->ZoneInfoDotNet->dwForwarderTimeout = zoneinfo->dwForwarderTimeout; + r->ZoneInfoDotNet->fForwarderSlave = zoneinfo->fForwarderSlave; + r->ZoneInfoDotNet->aipLocalMasters = ip4_array_copy(mem_ctx, zoneinfo->aipLocalMasters); + r->ZoneInfoDotNet->dwDpFlags = z->partition->dwDpFlags; + r->ZoneInfoDotNet->pszDpFqdn = talloc_strdup(mem_ctx, z->partition->pszDpFqdn); + r->ZoneInfoDotNet->pwszZoneDn = talloc_strdup(mem_ctx, zoneinfo->pwszZoneDn); + r->ZoneInfoDotNet->dwLastSuccessfulSoaCheck = zoneinfo->dwLastSuccessfulSoaCheck; + r->ZoneInfoDotNet->dwLastSuccessfulXfr = zoneinfo->dwLastSuccessfulXfr; + + } else { + *typeid = DNSSRV_TYPEID_ZONE_INFO; + r->ZoneInfo = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_INFO_LONGHORN); + + r->ZoneInfo->dwRpcStructureVersion = 0x02; + r->ZoneInfo->pszZoneName = talloc_strdup(mem_ctx, z->name); + r->ZoneInfo->dwZoneType = zoneinfo->dwZoneType; + r->ZoneInfo->fReverse = zoneinfo->fReverse; + r->ZoneInfo->fAllowUpdate = zoneinfo->fAllowUpdate; + r->ZoneInfo->fPaused = zoneinfo->fPaused; + r->ZoneInfo->fShutdown = zoneinfo->fShutdown; + r->ZoneInfo->fAutoCreated = zoneinfo->fAutoCreated; + r->ZoneInfo->fUseDatabase = zoneinfo->fUseDatabase; + r->ZoneInfo->pszDataFile = talloc_strdup(mem_ctx, zoneinfo->pszDataFile); + r->ZoneInfo->aipMasters = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipMasters); + r->ZoneInfo->fSecureSecondaries = zoneinfo->fSecureSecondaries; + r->ZoneInfo->fNotifyLevel = zoneinfo->fNotifyLevel; + r->ZoneInfo->aipSecondaries = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipSecondaries); + r->ZoneInfo->aipNotify = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipNotify); + r->ZoneInfo->fUseWins = zoneinfo->fUseWins; + r->ZoneInfo->fUseNbstat = zoneinfo->fUseNbstat; + r->ZoneInfo->fAging = zoneinfo->fAging; + r->ZoneInfo->dwNoRefreshInterval = zoneinfo->dwNoRefreshInterval; + r->ZoneInfo->dwRefreshInterval = zoneinfo->dwRefreshInterval; + r->ZoneInfo->dwAvailForScavengeTime = zoneinfo->dwAvailForScavengeTime; + r->ZoneInfo->aipScavengeServers = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipScavengeServers); + r->ZoneInfo->dwForwarderTimeout = zoneinfo->dwForwarderTimeout; + r->ZoneInfo->fForwarderSlave = zoneinfo->fForwarderSlave; + r->ZoneInfo->aipLocalMasters = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipLocalMasters); + r->ZoneInfo->dwDpFlags = z->partition->dwDpFlags; + r->ZoneInfo->pszDpFqdn = talloc_strdup(mem_ctx, z->partition->pszDpFqdn); + r->ZoneInfo->pwszZoneDn = talloc_strdup(mem_ctx, zoneinfo->pwszZoneDn); + r->ZoneInfo->dwLastSuccessfulSoaCheck = zoneinfo->dwLastSuccessfulSoaCheck; + r->ZoneInfo->dwLastSuccessfulXfr = zoneinfo->dwLastSuccessfulXfr; + + r->ZoneInfo->fQueuedForBackgroundLoad = zoneinfo->fQueuedForBackgroundLoad; + r->ZoneInfo->fBackgroundLoadInProgress = zoneinfo->fBackgroundLoadInProgress; + r->ZoneInfo->fReadOnlyZone = zoneinfo->fReadOnlyZone; + r->ZoneInfo->dwLastXfrAttempt = zoneinfo->dwLastXfrAttempt; + r->ZoneInfo->dwLastXfrResult = zoneinfo->dwLastXfrResult; + } + + return WERR_OK; + } + + is_integer = 0; + + if (strcasecmp(operation, "AllowUpdate") == 0) { + answer_integer = zoneinfo->fAllowUpdate; + is_integer = 1; + } else if (strcasecmp(operation, "Secured") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "DsIntegrated") == 0) { + answer_integer = zoneinfo->fUseDatabase; + is_integer = 1; + } else if (strcasecmp(operation, "LogUpdates") == 0) { + answer_integer = 0; + is_integer = 1; + } else if (strcasecmp(operation, "NoRefreshInterval") == 0) { + answer_integer = zoneinfo->dwNoRefreshInterval; + is_integer = 1; + } else if (strcasecmp(operation, "NotifyLevel") == 0) { + answer_integer = zoneinfo->fNotifyLevel; + is_integer = 1; + } else if (strcasecmp(operation, "RefreshInterval") == 0) { + answer_integer = zoneinfo->dwRefreshInterval; + is_integer = 1; + } else if (strcasecmp(operation, "SecureSecondaries") == 0) { + answer_integer = zoneinfo->fSecureSecondaries; + is_integer = 1; + } else if (strcasecmp(operation, "Type") == 0) { + answer_integer = zoneinfo->dwZoneType; + is_integer = 1; + } else if (strcasecmp(operation, "Aging") == 0) { + answer_integer = zoneinfo->fAging; + is_integer = 1; + } else if (strcasecmp(operation, "ForwarderSlave") == 0) { + answer_integer = zoneinfo->fForwarderSlave; + is_integer = 1; + } else if (strcasecmp(operation, "ForwarderTimeout") == 0) { + answer_integer = zoneinfo->dwForwarderTimeout; + is_integer = 1; + } else if (strcasecmp(operation, "Unicode") == 0) { + answer_integer = 0; + is_integer = 1; + } + + if (is_integer == 1) { + *typeid = DNSSRV_TYPEID_DWORD; + r->Dword = answer_integer; + return WERR_OK; + } + + is_addresses = 0; + + if (strcasecmp(operation, "AllowNSRecordsAutoCreation") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = NULL; + } else { + answer_iparray = NULL; + } + is_addresses = 1; + } else if (strcasecmp(operation, "ScavengeServers") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipScavengeServers); + } else { + answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipScavengeServers); + } + is_addresses = 1; + } else if (strcasecmp(operation, "MasterServers") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipMasters); + } else { + answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipMasters); + } + is_addresses = 1; + } else if (strcasecmp(operation, "LocalMasterServers") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipLocalMasters); + } else { + answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipLocalMasters); + } + is_addresses = 1; + } else if (strcasecmp(operation, "NotifyServers") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipNotify); + } else { + answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipNotify); + } + is_addresses = 1; + } else if (strcasecmp(operation, "SecondaryServers") == 0) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + answer_addrarray = ip4_array_to_dns_addr_array(mem_ctx, zoneinfo->aipSecondaries); + } else { + answer_iparray = ip4_array_copy(mem_ctx, zoneinfo->aipSecondaries); + } + is_addresses = 1; + } + + if (is_addresses == 1) { + if (client_version == DNS_CLIENT_VERSION_LONGHORN) { + *typeid = DNSSRV_TYPEID_ADDRARRAY; + r->AddrArray = answer_addrarray; + } else { + *typeid = DNSSRV_TYPEID_IPARRAY; + r->IpArray = answer_iparray; + } + return WERR_OK; + } + + is_string = 0; + + if (strcasecmp(operation, "DatabaseFile") == 0) { + answer_string = talloc_strdup(mem_ctx, zoneinfo->pszDataFile); + is_string = 1; + } else if (strcasecmp(operation, "ApplicationDirectoryPartition") == 0) { + answer_string = talloc_strdup(mem_ctx, z->partition->pszDpFqdn); + is_string = 1; + } else if (strcasecmp(operation, "BreakOnNameUpdate") == 0) { + answer_string = NULL; + is_string = 1; + } + + if (is_string == 1) { + *typeid = DNSSRV_TYPEID_LPSTR; + r->String = answer_string; + return WERR_OK; + } + + DEBUG(0,("dnsserver: Invalid zone operation %s", operation)); + return WERR_DNS_ERROR_INVALID_PROPERTY; + +} + +/* dnsserver operation functions */ + +/* [MS-DNSP].pdf Section 3.1.1.1 DNS Server Configuration Information */ +static WERROR dnsserver_operate_server(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + const char *operation, + const unsigned int client_version, + enum DNS_RPC_TYPEID typeid, + union DNSSRV_RPC_UNION *r) +{ + bool valid_operation = false; + + if (strcasecmp(operation, "ResetDwordProperty") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "Restart") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ClearDebugLog") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ClearCache") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "WriteDirtyZones") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ZoneCreate") == 0) { + struct dnsserver_zone *z, *z2; + WERROR status; + size_t len; + const char *name; + z = talloc_zero(mem_ctx, struct dnsserver_zone); + W_ERROR_HAVE_NO_MEMORY(z); + z->partition = talloc_zero(z, struct dnsserver_partition); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(z->partition, z); + z->zoneinfo = talloc_zero(z, struct dnsserver_zoneinfo); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(z->zoneinfo, z); + + if (typeid == DNSSRV_TYPEID_ZONE_CREATE_W2K) { + name = r->ZoneCreateW2K->pszZoneName; + z->zoneinfo->dwZoneType = r->ZoneCreateW2K->dwZoneType; + z->zoneinfo->fAllowUpdate = r->ZoneCreateW2K->fAllowUpdate; + z->zoneinfo->fAging = r->ZoneCreateW2K->fAging; + z->zoneinfo->Flags = r->ZoneCreateW2K->dwFlags; + } else if (typeid == DNSSRV_TYPEID_ZONE_CREATE_DOTNET) { + name = r->ZoneCreateDotNet->pszZoneName; + z->zoneinfo->dwZoneType = r->ZoneCreateDotNet->dwZoneType; + z->zoneinfo->fAllowUpdate = r->ZoneCreateDotNet->fAllowUpdate; + z->zoneinfo->fAging = r->ZoneCreateDotNet->fAging; + z->zoneinfo->Flags = r->ZoneCreateDotNet->dwFlags; + z->partition->dwDpFlags = r->ZoneCreateDotNet->dwDpFlags; + } else if (typeid == DNSSRV_TYPEID_ZONE_CREATE) { + name = r->ZoneCreate->pszZoneName; + z->zoneinfo->dwZoneType = r->ZoneCreate->dwZoneType; + z->zoneinfo->fAllowUpdate = r->ZoneCreate->fAllowUpdate; + z->zoneinfo->fAging = r->ZoneCreate->fAging; + z->zoneinfo->Flags = r->ZoneCreate->dwFlags; + z->partition->dwDpFlags = r->ZoneCreate->dwDpFlags; + } else { + talloc_free(z); + return WERR_DNS_ERROR_INVALID_PROPERTY; + } + + len = strlen(name); + if (name[len-1] == '.') { + len -= 1; + } + z->name = talloc_strndup(z, name, len); + if (z->name == NULL) { + talloc_free(z); + return WERR_NOT_ENOUGH_MEMORY; + } + + z2 = dnsserver_find_zone(dsstate->zones, z->name); + if (z2 != NULL) { + talloc_free(z); + return WERR_DNS_ERROR_ZONE_ALREADY_EXISTS; + } + + status = dnsserver_db_create_zone(dsstate->samdb, dsstate->partitions, z, + dsstate->lp_ctx); + talloc_free(z); + + if (W_ERROR_IS_OK(status)) { + dnsserver_reload_zones(dsstate); + } + return status; + } else if (strcasecmp(operation, "ClearStatistics") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "EnlistDirectoryPartition") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "StartScavenging") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "AbortScavenging") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "AutoConfigure") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ExportSettings") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "PrepareForDemotion") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "PrepareForUninstall") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DeleteNode") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DeleteRecord") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "WriteBackFile") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ListenAddresses") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "Forwarders") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "LogFilePath") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "LogIpFilterList") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ForestDirectoryPartitionBaseName") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DomainDirectoryPartitionBaseName") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "GlobalQueryBlockList") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "BreakOnReceiveFrom") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "BreakOnUpdateFrom") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ServerLevelPluginDll") == 0) { + valid_operation = true; + } + + if (valid_operation) { + DEBUG(0, ("dnsserver: server operation '%s' not implemented", operation)); + return WERR_CALL_NOT_IMPLEMENTED; + } + + DEBUG(0, ("dnsserver: invalid server operation '%s'", operation)); + return WERR_DNS_ERROR_INVALID_PROPERTY; +} + +static WERROR dnsserver_complex_operate_server(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + const char *operation, + const unsigned int client_version, + enum DNS_RPC_TYPEID typeid_in, + union DNSSRV_RPC_UNION *rin, + enum DNS_RPC_TYPEID *typeid_out, + union DNSSRV_RPC_UNION *rout) +{ + int valid_operation = 0; + struct dnsserver_zone *z, **zlist; + size_t zcount; + bool found1, found2, found3, found4; + size_t i; + + if (strcasecmp(operation, "QueryDwordProperty") == 0) { + if (typeid_in == DNSSRV_TYPEID_LPSTR) { + return dnsserver_query_server(dsstate, mem_ctx, + rin->String, + client_version, + typeid_out, + rout); + } + } else if (strcasecmp(operation, "EnumZones") == 0) { + if (typeid_in != DNSSRV_TYPEID_DWORD) { + return WERR_DNS_ERROR_INVALID_PROPERTY; + } + + zcount = 0; + zlist = talloc_zero_array(mem_ctx, struct dnsserver_zone *, 0); + for (z = dsstate->zones; z; z = z->next) { + + /* Match the flags in groups + * + * Group1 : PRIMARY, SECONDARY, CACHE, AUTO + * Group2 : FORWARD, REVERSE, FORWARDER, STUB + * Group3 : DS, NON_DS, DOMAIN_DP, FOREST_DP + * Group4 : CUSTOM_DP, LEGACY_DP + */ + + /* Group 1 */ + found1 = false; + if (rin->Dword & 0x0000000f) { + if (rin->Dword & DNS_ZONE_REQUEST_PRIMARY) { + if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) { + found1 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_SECONDARY) { + if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_SECONDARY) { + found1 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_CACHE) { + if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_CACHE) { + found1 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_AUTO) { + if (z->zoneinfo->fAutoCreated + || z->partition->dwDpFlags & DNS_DP_AUTOCREATED) { + found1 = true; + } + } + } else { + found1 = true; + } + + /* Group 2 */ + found2 = false; + if (rin->Dword & 0x000000f0) { + if (rin->Dword & DNS_ZONE_REQUEST_FORWARD) { + if (!(z->zoneinfo->fReverse)) { + found2 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_REVERSE) { + if (z->zoneinfo->fReverse) { + found2 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_FORWARDER) { + if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_FORWARDER) { + found2 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_STUB) { + if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_STUB) { + found2 = true; + } + } + } else { + found2 = true; + } + + /* Group 3 */ + found3 = false; + if (rin->Dword & 0x00000f00) { + if (rin->Dword & DNS_ZONE_REQUEST_DS) { + if (z->zoneinfo->Flags & DNS_RPC_ZONE_DSINTEGRATED) { + found3 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_NON_DS) { + if (!(z->zoneinfo->Flags & DNS_RPC_ZONE_DSINTEGRATED)) { + found3 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_DOMAIN_DP) { + if (!(z->partition->dwDpFlags & DNS_DP_DOMAIN_DEFAULT)) { + found3 = true; + } + } + if (rin->Dword & DNS_ZONE_REQUEST_FOREST_DP) { + if (!(z->partition->dwDpFlags & DNS_DP_FOREST_DEFAULT)) { + found3 = true; + } + } + } else { + found3 = true; + } + + /* Group 4 */ + if (rin->Dword & 0x0000f000) { + found4 = false; + } else { + found4 = true; + } + + if (found1 && found2 && found3 && found4) { + zlist = talloc_realloc(mem_ctx, zlist, struct dnsserver_zone *, zcount+1); + zlist[zcount] = z; + zcount++; + } + } + + if (client_version == DNS_CLIENT_VERSION_W2K) { + *typeid_out = DNSSRV_TYPEID_ZONE_LIST_W2K; + rout->ZoneListW2K = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_LIST_W2K); + + if (zcount == 0) { + rout->ZoneListW2K->dwZoneCount = 0; + rout->ZoneListW2K->ZoneArray = NULL; + + return WERR_OK; + } + + rout->ZoneListW2K->ZoneArray = talloc_zero_array(mem_ctx, struct DNS_RPC_ZONE_W2K *, zcount); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(rout->ZoneListW2K->ZoneArray, zlist); + + for (i=0; i<zcount; i++) { + rout->ZoneListW2K->ZoneArray[i] = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_W2K); + + rout->ZoneListW2K->ZoneArray[i]->pszZoneName = talloc_strdup(mem_ctx, zlist[i]->name); + rout->ZoneListW2K->ZoneArray[i]->Flags = zlist[i]->zoneinfo->Flags; + rout->ZoneListW2K->ZoneArray[i]->ZoneType = zlist[i]->zoneinfo->dwZoneType; + rout->ZoneListW2K->ZoneArray[i]->Version = zlist[i]->zoneinfo->Version; + } + rout->ZoneListW2K->dwZoneCount = zcount; + + } else { + *typeid_out = DNSSRV_TYPEID_ZONE_LIST; + rout->ZoneList = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_LIST_DOTNET); + + if (zcount == 0) { + rout->ZoneList->dwRpcStructureVersion = 1; + rout->ZoneList->dwZoneCount = 0; + rout->ZoneList->ZoneArray = NULL; + + return WERR_OK; + } + + rout->ZoneList->ZoneArray = talloc_zero_array(mem_ctx, struct DNS_RPC_ZONE_DOTNET *, zcount); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(rout->ZoneList->ZoneArray, zlist); + + for (i=0; i<zcount; i++) { + rout->ZoneList->ZoneArray[i] = talloc_zero(mem_ctx, struct DNS_RPC_ZONE_DOTNET); + + rout->ZoneList->ZoneArray[i]->dwRpcStructureVersion = 1; + rout->ZoneList->ZoneArray[i]->pszZoneName = talloc_strdup(mem_ctx, zlist[i]->name); + rout->ZoneList->ZoneArray[i]->Flags = zlist[i]->zoneinfo->Flags; + rout->ZoneList->ZoneArray[i]->ZoneType = zlist[i]->zoneinfo->dwZoneType; + rout->ZoneList->ZoneArray[i]->Version = zlist[i]->zoneinfo->Version; + rout->ZoneList->ZoneArray[i]->dwDpFlags = zlist[i]->partition->dwDpFlags; + rout->ZoneList->ZoneArray[i]->pszDpFqdn = talloc_strdup(mem_ctx, zlist[i]->partition->pszDpFqdn); + } + rout->ZoneList->dwRpcStructureVersion = 1; + rout->ZoneList->dwZoneCount = zcount; + } + talloc_free(zlist); + return WERR_OK; + } else if (strcasecmp(operation, "EnumZones2") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "EnumDirectoryPartitions") == 0) { + if (typeid_in != DNSSRV_TYPEID_DWORD) { + return WERR_DNS_ERROR_INVALID_PROPERTY; + } + + *typeid_out = DNSSRV_TYPEID_DP_LIST; + rout->DirectoryPartitionList = talloc_zero(mem_ctx, struct DNS_RPC_DP_LIST); + + if (rin->Dword != 0) { + rout->DirectoryPartitionList->dwDpCount = 0; + rout->DirectoryPartitionList->DpArray = NULL; + } else { + struct DNS_RPC_DP_ENUM **dplist; + struct dnsserver_partition *p; + int pcount = 2; + + dplist = talloc_zero_array(mem_ctx, struct DNS_RPC_DP_ENUM *, pcount); + W_ERROR_HAVE_NO_MEMORY(dplist); + + p = dsstate->partitions; + for (i=0; i<pcount; i++) { + dplist[i] = talloc_zero(dplist, struct DNS_RPC_DP_ENUM); + + dplist[i]->pszDpFqdn = talloc_strdup(mem_ctx, p->pszDpFqdn); + dplist[i]->dwFlags = p->dwDpFlags; + dplist[i]->dwZoneCount = p->zones_count; + p = p->next; + } + + rout->DirectoryPartitionList->dwDpCount = pcount; + rout->DirectoryPartitionList->DpArray = dplist; + } + return WERR_OK; + } else if (strcasecmp(operation, "DirectoryPartitionInfo") == 0) { + struct dnsserver_partition *p; + struct dnsserver_partition_info *partinfo; + struct DNS_RPC_DP_INFO *dpinfo = NULL; + + if (typeid_in != DNSSRV_TYPEID_LPSTR) { + return WERR_DNS_ERROR_INVALID_PROPERTY; + } + + *typeid_out = DNSSRV_TYPEID_DP_INFO; + + for (p = dsstate->partitions; p; p = p->next) { + if (strcasecmp(p->pszDpFqdn, rin->String) == 0) { + dpinfo = talloc_zero(mem_ctx, struct DNS_RPC_DP_INFO); + W_ERROR_HAVE_NO_MEMORY(dpinfo); + + partinfo = dnsserver_db_partition_info(mem_ctx, dsstate->samdb, p); + W_ERROR_HAVE_NO_MEMORY(partinfo); + + dpinfo->pszDpFqdn = talloc_strdup(dpinfo, p->pszDpFqdn); + dpinfo->pszDpDn = talloc_strdup(dpinfo, ldb_dn_get_linearized(p->partition_dn)); + dpinfo->pszCrDn = talloc_steal(dpinfo, partinfo->pszCrDn); + dpinfo->dwFlags = p->dwDpFlags; + dpinfo->dwZoneCount = p->zones_count; + dpinfo->dwState = partinfo->dwState; + dpinfo->dwReplicaCount = partinfo->dwReplicaCount; + if (partinfo->dwReplicaCount > 0) { + dpinfo->ReplicaArray = talloc_steal(dpinfo, + partinfo->ReplicaArray); + } else { + dpinfo->ReplicaArray = NULL; + } + break; + } + } + + if (dpinfo == NULL) { + return WERR_DNS_ERROR_DP_DOES_NOT_EXIST; + } + + rout->DirectoryPartition = dpinfo; + return WERR_OK; + } else if (strcasecmp(operation, "Statistics") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "IpValidate") == 0) { + valid_operation = true; + } + + if (valid_operation) { + DEBUG(0, ("dnsserver: server complex operation '%s' not implemented", operation)); + return WERR_CALL_NOT_IMPLEMENTED; + } + + DEBUG(0, ("dnsserver: invalid server complex operation '%s'", operation)); + return WERR_DNS_ERROR_INVALID_PROPERTY; +} + +/* [MS-DNSP].pdf Section 3.1.1.2 Zone Configuration Information */ +static WERROR dnsserver_operate_zone(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + struct dnsserver_zone *z, + unsigned int request_filter, + const char *operation, + const unsigned int client_version, + enum DNS_RPC_TYPEID typeid, + union DNSSRV_RPC_UNION *r) +{ + bool valid_operation = false; + + if (strcasecmp(operation, "ResetDwordProperty") == 0) { + + if (typeid != DNSSRV_TYPEID_NAME_AND_PARAM) { + return WERR_DNS_ERROR_INVALID_PROPERTY; + } + + return dnsserver_db_do_reset_dword(dsstate->samdb, z, + r->NameAndParam); + + } else if (strcasecmp(operation, "ZoneTypeReset") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "PauseZone") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ResumeZone") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DeleteZone") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ReloadZone") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "RefreshZone") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ExpireZone") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "IncrementVersion") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "WriteBackFile") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DeleteZoneFromDs") == 0) { + WERROR status; + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + status = dnsserver_db_delete_zone(dsstate->samdb, z); + if (W_ERROR_IS_OK(status)) { + dnsserver_reload_zones(dsstate); + } + return status; + } else if (strcasecmp(operation, "UpdateZoneFromDs") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ZoneExport") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ZoneChangeDirectoryPartition") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DeleteNode") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DeleteRecordSet") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ForceAgingOnNode") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "DatabaseFile") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "MasterServers") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "LocalMasterServers") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "NotifyServers") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "SecondaryServers") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ScavengingServers") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "AllowNSRecordsAutoCreation") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "BreakOnNameUpdate") == 0) { + valid_operation = true; + } else if (strcasecmp(operation, "ApplicationDirectoryPartition") == 0) { + valid_operation = true; + } + + if (valid_operation) { + DEBUG(0, ("dnsserver: zone operation '%s' not implemented", operation)); + return WERR_CALL_NOT_IMPLEMENTED; + } + + DEBUG(0, ("dnsserver: invalid zone operation '%s'", operation)); + return WERR_DNS_ERROR_INVALID_PROPERTY; +} + +static WERROR dnsserver_complex_operate_zone(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + struct dnsserver_zone *z, + const char *operation, + const unsigned int client_version, + enum DNS_RPC_TYPEID typeid_in, + union DNSSRV_RPC_UNION *rin, + enum DNS_RPC_TYPEID *typeid_out, + union DNSSRV_RPC_UNION *rout) +{ + if (strcasecmp(operation, "QueryDwordProperty") == 0) { + if (typeid_in == DNSSRV_TYPEID_LPSTR) { + return dnsserver_query_zone(dsstate, mem_ctx, z, + rin->String, + client_version, + typeid_out, + rout); + + } + } + + DEBUG(0,("dnsserver: Invalid zone operation %s", operation)); + return WERR_DNS_ERROR_INVALID_PROPERTY; +} + +/* dnsserver enumerate function */ + +static WERROR dnsserver_enumerate_root_records(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + unsigned int client_version, + const char *node_name, + enum dns_record_type record_type, + unsigned int select_flag, + unsigned int *buffer_length, + struct DNS_RPC_RECORDS_ARRAY **buffer) +{ + TALLOC_CTX *tmp_ctx; + struct dnsserver_zone *z; + const char * const attrs[] = { "name", "dnsRecord", NULL }; + struct ldb_result *res; + struct DNS_RPC_RECORDS_ARRAY *recs; + char **add_names; + char *rname; + int add_count; + int i, ret, len; + WERROR status; + + tmp_ctx = talloc_new(mem_ctx); + W_ERROR_HAVE_NO_MEMORY(tmp_ctx); + + z = dnsserver_find_zone(dsstate->zones, "."); + if (z == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=@)(!(dNSTombstoned=TRUE)))"); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return WERR_INTERNAL_DB_ERROR; + } + if (res->count == 0) { + talloc_free(tmp_ctx); + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + recs = talloc_zero(mem_ctx, struct DNS_RPC_RECORDS_ARRAY); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(recs, tmp_ctx); + + add_names = NULL; + add_count = 0; + + for (i=0; i<res->count; i++) { + status = dns_fill_records_array(tmp_ctx, NULL, record_type, + select_flag, NULL, + res->msgs[i], 0, recs, + &add_names, &add_count); + if (!W_ERROR_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + } + talloc_free(res); + + /* Add any additional records */ + if (select_flag & DNS_RPC_VIEW_ADDITIONAL_DATA) { + for (i=0; i<add_count; i++) { + char *encoded_name + = ldb_binary_encode_string(tmp_ctx, + add_names[i]); + ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))", + encoded_name); + if (ret != LDB_SUCCESS || res->count == 0) { + talloc_free(res); + continue; + } + + len = strlen(add_names[i]); + if (add_names[i][len-1] == '.') { + rname = talloc_strdup(tmp_ctx, add_names[i]); + } else { + rname = talloc_asprintf(tmp_ctx, "%s.", add_names[i]); + } + status = dns_fill_records_array(tmp_ctx, NULL, DNS_TYPE_A, + select_flag, rname, + res->msgs[0], 0, recs, + NULL, NULL); + talloc_free(rname); + talloc_free(res); + if (!W_ERROR_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + } + } + + talloc_free(tmp_ctx); + + *buffer_length = ndr_size_DNS_RPC_RECORDS_ARRAY(recs, 0); + *buffer = recs; + + return WERR_OK; +} + + +static WERROR dnsserver_enumerate_records(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + struct dnsserver_zone *z, + unsigned int client_version, + const char *node_name, + const char *start_child, + enum dns_record_type record_type, + unsigned int select_flag, + const char *filter_start, + const char *filter_stop, + unsigned int *buffer_length, + struct DNS_RPC_RECORDS_ARRAY **buffer) +{ + TALLOC_CTX *tmp_ctx; + char *name; + const char * const attrs[] = { "name", "dnsRecord", NULL }; + struct ldb_result *res = NULL; + struct DNS_RPC_RECORDS_ARRAY *recs = NULL; + char **add_names = NULL; + char *rname = NULL; + const char *preference_name = NULL; + int add_count = 0; + int i, ret, len; + WERROR status; + struct dns_tree *tree = NULL; + struct dns_tree *base = NULL; + struct dns_tree *node = NULL; + + tmp_ctx = talloc_new(mem_ctx); + W_ERROR_HAVE_NO_MEMORY(tmp_ctx); + + name = dns_split_node_name(tmp_ctx, node_name, z->name); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(name, tmp_ctx); + + /* search all records under parent tree */ + if (strcasecmp(name, z->name) == 0) { + ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))"); + preference_name = "@"; + } else { + char *encoded_name + = ldb_binary_encode_string(tmp_ctx, name); + ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z->zone_dn, + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(|(name=%s)(name=*.%s))(!(dNSTombstoned=TRUE)))", + encoded_name, encoded_name); + preference_name = name; + } + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return WERR_INTERNAL_DB_ERROR; + } + if (res->count == 0) { + talloc_free(tmp_ctx); + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + recs = talloc_zero(mem_ctx, struct DNS_RPC_RECORDS_ARRAY); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(recs, tmp_ctx); + + /* + * Sort the names, so that the records are in order by the child + * component below "name". + * + * A full tree sort is not required, so we pass in "name" so + * we know which level to sort, as only direct children are + * eventually returned + */ + LDB_TYPESAFE_QSORT(res->msgs, res->count, name, dns_name_compare); + + /* Build a tree of name components from dns name */ + tree = dns_build_tree(tmp_ctx, preference_name, res); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(tree, tmp_ctx); + + /* Find the parent record in the tree */ + base = tree; + while (base->level != -1) { + base = base->children[0]; + } + + /* Add the parent record with blank name */ + if (!(select_flag & DNS_RPC_VIEW_ONLY_CHILDREN)) { + status = dns_fill_records_array(tmp_ctx, z, record_type, + select_flag, NULL, + base->data, 0, + recs, &add_names, &add_count); + if (!W_ERROR_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + } + + /* Add all the children records */ + if (!(select_flag & DNS_RPC_VIEW_NO_CHILDREN)) { + for (i=0; i<base->num_children; i++) { + node = base->children[i]; + + status = dns_fill_records_array(tmp_ctx, z, record_type, + select_flag, node->name, + node->data, node->num_children, + recs, &add_names, &add_count); + if (!W_ERROR_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + } + } + + TALLOC_FREE(res); + TALLOC_FREE(tree); + TALLOC_FREE(name); + + /* Add any additional records */ + if (select_flag & DNS_RPC_VIEW_ADDITIONAL_DATA) { + for (i=0; i<add_count; i++) { + struct dnsserver_zone *z2 = NULL; + struct ldb_message *msg = NULL; + /* Search all the available zones for additional name */ + for (z2 = dsstate->zones; z2; z2 = z2->next) { + char *encoded_name; + name = dns_split_node_name(tmp_ctx, add_names[i], z2->name); + encoded_name + = ldb_binary_encode_string(tmp_ctx, + name); + ret = ldb_search(dsstate->samdb, tmp_ctx, &res, z2->zone_dn, + LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))", + encoded_name); + TALLOC_FREE(name); + if (ret != LDB_SUCCESS) { + continue; + } + if (res->count == 1) { + msg = res->msgs[0]; + break; + } else { + TALLOC_FREE(res); + continue; + } + } + + len = strlen(add_names[i]); + if (add_names[i][len-1] == '.') { + rname = talloc_strdup(tmp_ctx, add_names[i]); + } else { + rname = talloc_asprintf(tmp_ctx, "%s.", add_names[i]); + } + status = dns_fill_records_array(tmp_ctx, NULL, DNS_TYPE_A, + select_flag, rname, + msg, 0, recs, + NULL, NULL); + TALLOC_FREE(rname); + TALLOC_FREE(res); + if (!W_ERROR_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + } + } + + *buffer_length = ndr_size_DNS_RPC_RECORDS_ARRAY(recs, 0); + *buffer = recs; + + return WERR_OK; +} + +/* + * Check str1 + '.' + str2 = name, for example: + * ("dc0", "example.com", "dc0.example.com") = true + */ +static bool cname_self_reference(const char* node_name, + const char* zone_name, + struct DNS_RPC_NAME name) { + size_t node_len, zone_len; + + if (node_name == NULL || zone_name == NULL) { + return false; + } + + node_len = strlen(node_name); + zone_len = strlen(zone_name); + + if (node_len == 0 || + zone_len == 0 || + (name.len != node_len + zone_len + 1)) { + return false; + } + + if (strncmp(node_name, name.str, node_len) == 0 && + name.str[node_len] == '.' && + strncmp(zone_name, name.str + node_len + 1, zone_len) == 0) { + return true; + } + + return false; +} + +/* dnsserver update function */ + +static WERROR dnsserver_update_record(struct dnsserver_state *dsstate, + TALLOC_CTX *mem_ctx, + struct dnsserver_zone *z, + unsigned int client_version, + const char *node_name, + struct DNS_RPC_RECORD_BUF *add_buf, + struct DNS_RPC_RECORD_BUF *del_buf) +{ + TALLOC_CTX *tmp_ctx; + char *name; + WERROR status; + + tmp_ctx = talloc_new(mem_ctx); + W_ERROR_HAVE_NO_MEMORY(tmp_ctx); + + /* If node_name is @ or zone name, dns record is @ */ + if (strcmp(node_name, "@") == 0 || + strcmp(node_name, ".") == 0 || + strcasecmp(node_name, z->name) == 0) { + name = talloc_strdup(tmp_ctx, "@"); + } else { + name = dns_split_node_name(tmp_ctx, node_name, z->name); + } + W_ERROR_HAVE_NO_MEMORY_AND_FREE(name, tmp_ctx); + + /* CNAMEs can't point to themselves */ + if (add_buf != NULL && add_buf->rec.wType == DNS_TYPE_CNAME) { + if (cname_self_reference(node_name, z->name, add_buf->rec.data.name)) { + return WERR_DNS_ERROR_CNAME_LOOP; + } + } + + if (add_buf != NULL) { + if (del_buf == NULL) { + /* Add record */ + status = dnsserver_db_add_record(tmp_ctx, dsstate->samdb, + z, name, + &add_buf->rec); + } else { + /* Update record */ + status = dnsserver_db_update_record(tmp_ctx, dsstate->samdb, + z, name, + &add_buf->rec, + &del_buf->rec); + } + } else { + if (del_buf == NULL) { + /* Add empty node */ + status = dnsserver_db_add_empty_node(tmp_ctx, dsstate->samdb, + z, name); + } else { + /* Delete record */ + status = dnsserver_db_delete_record(tmp_ctx, dsstate->samdb, + z, name, + &del_buf->rec); + } + } + + talloc_free(tmp_ctx); + return status; +} + + +/* dnsserver interface functions */ + +static WERROR dcesrv_DnssrvOperation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvOperation *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z = NULL; + uint32_t request_filter = 0; + WERROR ret; + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.dwContext == 0) { + if (r->in.pszZone != NULL) { + request_filter = dnsserver_zone_to_request_filter(r->in.pszZone); + } + } else { + request_filter = r->in.dwContext; + } + + if (r->in.pszZone == NULL) { + ret = dnsserver_operate_server(dsstate, mem_ctx, + r->in.pszOperation, + DNS_CLIENT_VERSION_W2K, + r->in.dwTypeId, + &r->in.pData); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + /* + * In the case that request_filter is not 0 and z is NULL, + * the request is for a multizone operation, which we do not + * yet support, so just error on NULL zone name. + */ + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + + ret = dnsserver_operate_zone(dsstate, mem_ctx, z, + request_filter, + r->in.pszOperation, + DNS_CLIENT_VERSION_W2K, + r->in.dwTypeId, + &r->in.pData); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvOperation, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvQuery(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvQuery *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + ZERO_STRUCTP(r->out.pdwTypeId); + ZERO_STRUCTP(r->out.ppData); + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + /* FIXME: DNS Server Configuration Access Control List */ + ret = dnsserver_query_server(dsstate, mem_ctx, + r->in.pszOperation, + DNS_CLIENT_VERSION_W2K, + r->out.pdwTypeId, + r->out.ppData); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + + ret = dnsserver_query_zone(dsstate, mem_ctx, z, + r->in.pszOperation, + DNS_CLIENT_VERSION_W2K, + r->out.pdwTypeId, + r->out.ppData); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvQuery, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvComplexOperation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvComplexOperation *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + ZERO_STRUCTP(r->out.pdwTypeOut); + ZERO_STRUCTP(r->out.ppDataOut); + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + /* Server operation */ + ret = dnsserver_complex_operate_server(dsstate, mem_ctx, + r->in.pszOperation, + DNS_CLIENT_VERSION_W2K, + r->in.dwTypeIn, + &r->in.pDataIn, + r->out.pdwTypeOut, + r->out.ppDataOut); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + + ret = dnsserver_complex_operate_zone(dsstate, mem_ctx, z, + r->in.pszOperation, + DNS_CLIENT_VERSION_W2K, + r->in.dwTypeIn, + &r->in.pDataIn, + r->out.pdwTypeOut, + r->out.ppDataOut); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvComplexOperation, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvEnumRecords(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvEnumRecords *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + ZERO_STRUCTP(r->out.pdwBufferLength); + ZERO_STRUCTP(r->out.pBuffer); + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + if (strcasecmp(r->in.pszZone, "..RootHints") == 0) { + ret = dnsserver_enumerate_root_records(dsstate, mem_ctx, + DNS_CLIENT_VERSION_W2K, + r->in.pszNodeName, + r->in.wRecordType, + r->in.fSelectFlag, + r->out.pdwBufferLength, + r->out.pBuffer); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + ret = dnsserver_enumerate_records(dsstate, mem_ctx, z, + DNS_CLIENT_VERSION_W2K, + r->in.pszNodeName, + r->in.pszStartChild, + r->in.wRecordType, + r->in.fSelectFlag, + r->in.pszFilterStart, + r->in.pszFilterStop, + r->out.pdwBufferLength, + r->out.pBuffer); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvEnumRecords, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvUpdateRecord(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvUpdateRecord *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + ret = dnsserver_update_record(dsstate, mem_ctx, z, + DNS_CLIENT_VERSION_W2K, + r->in.pszNodeName, + r->in.pAddRecord, + r->in.pDeleteRecord); + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvUpdateRecord, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvOperation2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvOperation2 *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z = NULL; + uint32_t request_filter = 0; + WERROR ret; + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.dwContext == 0) { + if (r->in.pszZone != NULL) { + request_filter = dnsserver_zone_to_request_filter(r->in.pszZone); + } + } else { + request_filter = r->in.dwContext; + } + + if (r->in.pszZone == NULL) { + ret = dnsserver_operate_server(dsstate, mem_ctx, + r->in.pszOperation, + r->in.dwClientVersion, + r->in.dwTypeId, + &r->in.pData); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + /* + * In the case that request_filter is not 0 and z is NULL, + * the request is for a multizone operation, which we do not + * yet support, so just error on NULL zone name. + */ + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + + ret = dnsserver_operate_zone(dsstate, mem_ctx, z, + request_filter, + r->in.pszOperation, + r->in.dwClientVersion, + r->in.dwTypeId, + &r->in.pData); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvOperation2, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvQuery2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvQuery2 *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + ZERO_STRUCTP(r->out.pdwTypeId); + ZERO_STRUCTP(r->out.ppData); + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + /* FIXME: DNS Server Configuration Access Control List */ + ret = dnsserver_query_server(dsstate, mem_ctx, + r->in.pszOperation, + r->in.dwClientVersion, + r->out.pdwTypeId, + r->out.ppData); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + + ret = dnsserver_query_zone(dsstate, mem_ctx, z, + r->in.pszOperation, + r->in.dwClientVersion, + r->out.pdwTypeId, + r->out.ppData); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvQuery2, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvComplexOperation2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvComplexOperation2 *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + ZERO_STRUCTP(r->out.pdwTypeOut); + ZERO_STRUCTP(r->out.ppDataOut); + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + /* Server operation */ + ret = dnsserver_complex_operate_server(dsstate, mem_ctx, + r->in.pszOperation, + r->in.dwClientVersion, + r->in.dwTypeIn, + &r->in.pDataIn, + r->out.pdwTypeOut, + r->out.ppDataOut); + } else { + + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST; + } + + ret = dnsserver_complex_operate_zone(dsstate, mem_ctx, z, + r->in.pszOperation, + r->in.dwClientVersion, + r->in.dwTypeIn, + &r->in.pDataIn, + r->out.pdwTypeOut, + r->out.ppDataOut); + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvComplexOperation2, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvEnumRecords2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvEnumRecords2 *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + ZERO_STRUCTP(r->out.pdwBufferLength); + ZERO_STRUCTP(r->out.pBuffer); + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + if (strcasecmp(r->in.pszZone, "..RootHints") == 0) { + ret = dnsserver_enumerate_root_records(dsstate, mem_ctx, + r->in.dwClientVersion, + r->in.pszNodeName, + r->in.wRecordType, + r->in.fSelectFlag, + r->out.pdwBufferLength, + r->out.pBuffer); + } else { + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + ret = dnsserver_enumerate_records(dsstate, mem_ctx, z, + r->in.dwClientVersion, + r->in.pszNodeName, + r->in.pszStartChild, + r->in.wRecordType, + r->in.fSelectFlag, + r->in.pszFilterStart, + r->in.pszFilterStop, + r->out.pdwBufferLength, + r->out.pBuffer); + + } + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvEnumRecords2, NDR_IN, r); + } + return ret; +} + +static WERROR dcesrv_DnssrvUpdateRecord2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct DnssrvUpdateRecord2 *r) +{ + struct dnsserver_state *dsstate; + struct dnsserver_zone *z; + WERROR ret; + + if ((dsstate = dnsserver_connect(dce_call)) == NULL) { + return WERR_DNS_ERROR_DS_UNAVAILABLE; + } + + if (r->in.pszZone == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + z = dnsserver_find_zone(dsstate->zones, r->in.pszZone); + if (z == NULL) { + return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST; + } + + ret = dnsserver_update_record(dsstate, mem_ctx, z, + r->in.dwClientVersion, + r->in.pszNodeName, + r->in.pAddRecord, + r->in.pDeleteRecord); + + if (W_ERROR_EQUAL(ret, WERR_CALL_NOT_IMPLEMENTED)) { + NDR_PRINT_FUNCTION_DEBUG(DnssrvUpdateRecord2, NDR_IN, r); + } + return ret; +} + +/* include the generated boilerplate */ +#include "librpc/gen_ndr/ndr_dnsserver_s.c" diff --git a/source4/rpc_server/dnsserver/dnsdata.c b/source4/rpc_server/dnsserver/dnsdata.c new file mode 100644 index 0000000..002d9e6 --- /dev/null +++ b/source4/rpc_server/dnsserver/dnsdata.c @@ -0,0 +1,1121 @@ +/* + 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 "dns_server/dnsserver_common.h" +#include "lib/replace/system/network.h" +#include "librpc/gen_ndr/ndr_dnsp.h" +#include "librpc/gen_ndr/ndr_dnsserver.h" + +#undef strcasecmp + +struct IP4_ARRAY *ip4_array_copy(TALLOC_CTX *mem_ctx, struct IP4_ARRAY *ip4) +{ + struct IP4_ARRAY *ret; + + if (!ip4) { + return NULL; + } + + ret = talloc_zero(mem_ctx, struct IP4_ARRAY); + if (!ret) { + return ret; + } + + ret->AddrCount = ip4->AddrCount; + if (ip4->AddrCount > 0) { + ret->AddrArray = talloc_zero_array(mem_ctx, unsigned int, ip4->AddrCount); + if (ret->AddrArray) { + memcpy(ret->AddrArray, ip4->AddrArray, + sizeof(unsigned int) * ip4->AddrCount); + } else { + talloc_free(ret); + return NULL; + } + } + return ret; +} + + +struct DNS_ADDR_ARRAY *ip4_array_to_dns_addr_array(TALLOC_CTX *mem_ctx, + struct IP4_ARRAY *ip4) +{ + struct DNS_ADDR_ARRAY *ret; + int i; + + if (!ip4) { + return NULL; + } + + ret = talloc_zero(mem_ctx, struct DNS_ADDR_ARRAY); + if (!ret) { + return ret; + } + + ret->MaxCount = ip4->AddrCount; + ret->AddrCount = ip4->AddrCount; + ret->Family = AF_INET; + if (ip4->AddrCount > 0) { + ret->AddrArray = talloc_zero_array(mem_ctx, struct DNS_ADDR, ip4->AddrCount); + if (ret->AddrArray) { + for (i=0; i<ip4->AddrCount; i++) { + ret->AddrArray[i].MaxSa[0] = 0x02; + ret->AddrArray[i].MaxSa[3] = 53; + memcpy(&ret->AddrArray[i].MaxSa[4], ip4->AddrArray, + sizeof(unsigned int)); + ret->AddrArray[i].DnsAddrUserDword[0] = 6; + } + + } else { + talloc_free(ret); + return NULL; + } + } + return ret; +} + +struct IP4_ARRAY *dns_addr_array_to_ip4_array(TALLOC_CTX *mem_ctx, + struct DNS_ADDR_ARRAY *ip) +{ + struct IP4_ARRAY *ret; + size_t i, count, curr; + + if (ip == NULL) { + return NULL; + } + /* We must only return IPv4 addresses. + The passed DNS_ADDR_ARRAY may contain: + - only ipv4 addresses + - only ipv6 addresses + - a mixture of both + - an empty array + */ + ret = talloc_zero(mem_ctx, struct IP4_ARRAY); + if (!ret) { + return ret; + } + if (ip->AddrCount == 0 || ip->Family == AF_INET6) { + ret->AddrCount = 0; + return ret; + } + /* Now only ipv4 addresses or a mixture are left */ + count = 0; + for (i = 0; i < ip->AddrCount; i++) { + if (ip->AddrArray[i].MaxSa[0] == 0x02) { + /* Is ipv4 */ + count++; + } + } + if (count == 0) { + /* should not happen */ + ret->AddrCount = 0; + return ret; + } + ret->AddrArray = talloc_zero_array(mem_ctx, uint32_t, count); + if (ret->AddrArray) { + curr = 0; + for (i = 0; i < ip->AddrCount; i++) { + if (ip->AddrArray[i].MaxSa[0] == 0x02) { + /* Is ipv4 */ + memcpy(&ret->AddrArray[curr], + &ip->AddrArray[i].MaxSa[4], + sizeof(uint32_t)); + curr++; + } + } + } else { + talloc_free(ret); + return NULL; + } + ret->AddrCount = curr; + return ret; +} + +struct DNS_ADDR_ARRAY *dns_addr_array_copy(TALLOC_CTX *mem_ctx, + struct DNS_ADDR_ARRAY *addr) +{ + struct DNS_ADDR_ARRAY *ret; + + if (!addr) { + return NULL; + } + + ret = talloc_zero(mem_ctx, struct DNS_ADDR_ARRAY); + if (!ret) { + return ret; + } + + ret->MaxCount = addr->MaxCount; + ret->AddrCount = addr->AddrCount; + ret->Family = addr->Family; + if (addr->AddrCount > 0) { + ret->AddrArray = talloc_zero_array(mem_ctx, struct DNS_ADDR, addr->AddrCount); + if (ret->AddrArray) { + memcpy(ret->AddrArray, addr->AddrArray, + sizeof(struct DNS_ADDR) * addr->AddrCount); + } else { + talloc_free(ret); + return NULL; + } + } + return ret; +} + + +int dns_split_name_components(TALLOC_CTX *tmp_ctx, const char *name, char ***components) +{ + char *str = NULL, *ptr, **list; + int count = 0; + + if (name == NULL) { + return 0; + } + + str = talloc_strdup(tmp_ctx, name); + if (!str) { + goto failed; + } + + list = talloc_zero_array(tmp_ctx, char *, 0); + if (!list) { + goto failed; + } + + ptr = strtok(str, "."); + while (ptr != NULL) { + count++; + list = talloc_realloc(tmp_ctx, list, char *, count); + if (!list) { + goto failed; + } + list[count-1] = talloc_strdup(tmp_ctx, ptr); + if (list[count-1] == NULL) { + goto failed; + } + ptr = strtok(NULL, "."); + } + + talloc_free(str); + + *components = list; + return count; + +failed: + TALLOC_FREE(str); + return -1; +} + + +char *dns_split_node_name(TALLOC_CTX *tmp_ctx, const char *node_name, const char *zone_name) +{ + char **nlist, **zlist; + char *prefix; + int ncount, zcount, i, match; + + /* + * If node_name is "@", return the zone_name + * If node_name is ".", return NULL + * If there is no '.' in node_name, return the node_name as is. + * + * If node_name does not have zone_name in it, return the node_name as is. + * + * If node_name has additional components as compared to zone_name + * return only the additional components as a prefix. + * + */ + if (strcmp(node_name, "@") == 0) { + prefix = talloc_strdup(tmp_ctx, zone_name); + } else if (strcmp(node_name, ".") == 0) { + prefix = NULL; + } else if (strchr(node_name, '.') == NULL) { + prefix = talloc_strdup(tmp_ctx, node_name); + } else { + zcount = dns_split_name_components(tmp_ctx, zone_name, &zlist); + ncount = dns_split_name_components(tmp_ctx, node_name, &nlist); + if (zcount < 0 || ncount < 0) { + return NULL; + } + + if (ncount < zcount) { + prefix = talloc_strdup(tmp_ctx, node_name); + } else { + match = 0; + for (i=1; i<=zcount; i++) { + if (strcasecmp(nlist[ncount-i], zlist[zcount-i]) != 0) { + break; + } + match++; + } + + if (match == ncount) { + prefix = talloc_strdup(tmp_ctx, zone_name); + } else { + prefix = talloc_strdup(tmp_ctx, nlist[0]); + if (prefix != NULL) { + for (i=1; i<ncount-match; i++) { + prefix = talloc_asprintf_append(prefix, ".%s", nlist[i]); + if (prefix == NULL) { + break; + } + } + } + } + } + + talloc_free(zlist); + talloc_free(nlist); + } + + return prefix; +} + + +void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp, + struct DNS_RPC_RECORD *dns) +{ + int i, len; + + ZERO_STRUCTP(dns); + + dns->wDataLength = dnsp->wDataLength; + dns->wType = dnsp->wType; + dns->dwFlags = dnsp->rank; + dns->dwSerial = dnsp->dwSerial; + dns->dwTtlSeconds = dnsp->dwTtlSeconds; + dns->dwTimeStamp = dnsp->dwTimeStamp; + + switch (dnsp->wType) { + + case DNS_TYPE_TOMBSTONE: + dns->data.EntombedTime = dnsp->data.EntombedTime; + break; + + case DNS_TYPE_A: + dns->data.ipv4 = talloc_strdup(mem_ctx, dnsp->data.ipv4); + break; + + case DNS_TYPE_NS: + len = strlen(dnsp->data.ns); + if (dnsp->data.ns[len-1] == '.') { + dns->data.name.len = len; + dns->data.name.str = talloc_strdup(mem_ctx, dnsp->data.ns); + } else { + dns->data.name.len = len+1; + dns->data.name.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.ns); + } + break; + + case DNS_TYPE_CNAME: + len = strlen(dnsp->data.cname); + if (dnsp->data.cname[len-1] == '.') { + dns->data.name.len = len; + dns->data.name.str = talloc_strdup(mem_ctx, dnsp->data.cname); + } else { + dns->data.name.len = len+1; + dns->data.name.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.cname); + } + break; + + case DNS_TYPE_SOA: + dns->data.soa.dwSerialNo = dnsp->data.soa.serial; + dns->data.soa.dwRefresh = dnsp->data.soa.refresh; + dns->data.soa.dwRetry = dnsp->data.soa.retry; + dns->data.soa.dwExpire = dnsp->data.soa.expire; + dns->data.soa.dwMinimumTtl = dnsp->data.soa.minimum; + + len = strlen(dnsp->data.soa.mname); + if (dnsp->data.soa.mname[len-1] == '.') { + dns->data.soa.NamePrimaryServer.len = len; + dns->data.soa.NamePrimaryServer.str = talloc_strdup(mem_ctx, dnsp->data.soa.mname); + } else { + dns->data.soa.NamePrimaryServer.len = len+1; + dns->data.soa.NamePrimaryServer.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.soa.mname); + } + + len = strlen(dnsp->data.soa.rname); + if (dnsp->data.soa.rname[len-1] == '.') { + dns->data.soa.ZoneAdministratorEmail.len = len; + dns->data.soa.ZoneAdministratorEmail.str = talloc_strdup(mem_ctx, dnsp->data.soa.rname); + } else { + dns->data.soa.ZoneAdministratorEmail.len = len+1; + dns->data.soa.ZoneAdministratorEmail.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.soa.rname); + } + break; + + case DNS_TYPE_PTR: + dns->data.ptr.len = strlen(dnsp->data.ptr); + dns->data.ptr.str = talloc_strdup(mem_ctx, dnsp->data.ptr); + break; + + case DNS_TYPE_MX: + dns->data.mx.wPreference = dnsp->data.mx.wPriority; + len = strlen(dnsp->data.mx.nameTarget); + if (dnsp->data.mx.nameTarget[len-1] == '.') { + dns->data.mx.nameExchange.len = len; + dns->data.mx.nameExchange.str = talloc_strdup(mem_ctx, dnsp->data.mx.nameTarget); + } else { + dns->data.mx.nameExchange.len = len+1; + dns->data.mx.nameExchange.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.mx.nameTarget); + } + break; + + case DNS_TYPE_TXT: + dns->data.txt.count = dnsp->data.txt.count; + dns->data.txt.str = talloc_array(mem_ctx, struct DNS_RPC_NAME, dnsp->data.txt.count); + for (i=0; i<dnsp->data.txt.count; i++) { + dns->data.txt.str[i].str = talloc_strdup(mem_ctx, dnsp->data.txt.str[i]); + dns->data.txt.str[i].len = strlen(dnsp->data.txt.str[i]); + } + break; + + case DNS_TYPE_AAAA: + dns->data.ipv6 = talloc_strdup(mem_ctx, dnsp->data.ipv6); + break; + + case DNS_TYPE_SRV: + dns->data.srv.wPriority = dnsp->data.srv.wPriority; + dns->data.srv.wWeight = dnsp->data.srv.wWeight; + dns->data.srv.wPort = dnsp->data.srv.wPort; + len = strlen(dnsp->data.srv.nameTarget); + if (dnsp->data.srv.nameTarget[len-1] == '.') { + dns->data.srv.nameTarget.len = len; + dns->data.srv.nameTarget.str = talloc_strdup(mem_ctx, dnsp->data.srv.nameTarget); + } else { + dns->data.srv.nameTarget.len = len+1; + dns->data.srv.nameTarget.str = talloc_asprintf(mem_ctx, "%s.", dnsp->data.srv.nameTarget); + } + break; + + default: + memcpy(&dns->data, &dnsp->data, sizeof(union DNS_RPC_RECORD_DATA)); + DEBUG(0, ("dnsserver: Found Unhandled DNS record type=%d", dnsp->wType)); + } + +} + +WERROR dns_to_dnsp_convert(TALLOC_CTX *mem_ctx, struct DNS_RPC_RECORD *dns, + struct dnsp_DnssrvRpcRecord **out_dnsp, bool check_name) +{ + WERROR res; + int i, len; + const char *name; + char *talloc_res = NULL; + struct dnsp_DnssrvRpcRecord *dnsp = NULL; + + dnsp = talloc_zero(mem_ctx, struct dnsp_DnssrvRpcRecord); + if (dnsp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + dnsp->wDataLength = dns->wDataLength; + dnsp->wType = dns->wType; + dnsp->version = 5; + dnsp->rank = dns->dwFlags & 0x000000FF; + dnsp->dwSerial = dns->dwSerial; + dnsp->dwTtlSeconds = dns->dwTtlSeconds; + dnsp->dwTimeStamp = dns->dwTimeStamp; + + switch (dns->wType) { + + case DNS_TYPE_TOMBSTONE: + dnsp->data.EntombedTime = dns->data.EntombedTime; + break; + + case DNS_TYPE_A: + talloc_res = talloc_strdup(mem_ctx, dns->data.ipv4); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.ipv4 = talloc_res; + break; + + case DNS_TYPE_NS: + name = dns->data.name.str; + len = dns->data.name.len; + + if (check_name) { + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + } + + if (len > 0 && name[len-1] == '.') { + talloc_res = talloc_strndup(mem_ctx, name, len-1); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.ns = talloc_res; + } else { + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.ns = talloc_res; + } + + break; + + case DNS_TYPE_CNAME: + name = dns->data.name.str; + len = dns->data.name.len; + + if (check_name) { + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + } + + if (len > 0 && name[len-1] == '.') { + talloc_res = talloc_strndup(mem_ctx, name, len-1); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.cname = talloc_res; + } else { + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.cname = talloc_res; + } + + break; + + case DNS_TYPE_SOA: + dnsp->data.soa.serial = dns->data.soa.dwSerialNo; + dnsp->data.soa.refresh = dns->data.soa.dwRefresh; + dnsp->data.soa.retry = dns->data.soa.dwRetry; + dnsp->data.soa.expire = dns->data.soa.dwExpire; + dnsp->data.soa.minimum = dns->data.soa.dwMinimumTtl; + + name = dns->data.soa.NamePrimaryServer.str; + len = dns->data.soa.NamePrimaryServer.len; + + if (check_name) { + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + } + + if (len > 0 && name[len-1] == '.') { + talloc_res = talloc_strndup(mem_ctx, name, len-1); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.soa.mname = talloc_res; + } else { + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.soa.mname = talloc_res; + } + + name = dns->data.soa.ZoneAdministratorEmail.str; + len = dns->data.soa.ZoneAdministratorEmail.len; + + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + + if (len > 0 && name[len-1] == '.') { + talloc_res = talloc_strndup(mem_ctx, name, len-1); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.soa.rname = talloc_res; + } else { + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.soa.rname = talloc_res; + } + + break; + + case DNS_TYPE_PTR: + name = dns->data.ptr.str; + len = dns->data.ptr.len; + + if (check_name) { + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + } + + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.ptr = talloc_res; + + break; + + case DNS_TYPE_MX: + dnsp->data.mx.wPriority = dns->data.mx.wPreference; + + name = dns->data.mx.nameExchange.str; + len = dns->data.mx.nameExchange.len; + + if (check_name) { + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + } + + if (len > 0 && name[len-1] == '.') { + talloc_res = talloc_strndup(mem_ctx, name, len-1); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.mx.nameTarget = talloc_res; + } else { + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.mx.nameTarget = talloc_res; + } + + break; + + case DNS_TYPE_TXT: + dnsp->data.txt.count = dns->data.txt.count; + dnsp->data.txt.str = talloc_array(mem_ctx, const char *, dns->data.txt.count); + for (i=0; i<dns->data.txt.count; i++) { + talloc_res = talloc_strdup(mem_ctx, dns->data.txt.str[i].str); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.txt.str[i] = talloc_res; + } + break; + + case DNS_TYPE_AAAA: + dnsp->data.ipv6 = talloc_strdup(mem_ctx, dns->data.ipv6); + break; + + case DNS_TYPE_SRV: + dnsp->data.srv.wPriority = dns->data.srv.wPriority; + dnsp->data.srv.wWeight = dns->data.srv.wWeight; + dnsp->data.srv.wPort = dns->data.srv.wPort; + + name = dns->data.srv.nameTarget.str; + len = dns->data.srv.nameTarget.len; + + if (check_name) { + res = dns_name_check(mem_ctx, len, name); + if (!W_ERROR_IS_OK(res)) { + return res; + } + } + + if (len > 0 && name[len-1] == '.') { + talloc_res = talloc_strndup(mem_ctx, name, len-1); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.srv.nameTarget = talloc_res; + } else { + talloc_res = talloc_strdup(mem_ctx, name); + if (talloc_res == NULL) { + goto fail_nomemory; + } + dnsp->data.srv.nameTarget = talloc_res; + } + + break; + + default: + memcpy(&dnsp->data, &dns->data, sizeof(union dnsRecordData)); + DEBUG(0, ("dnsserver: Found Unhandled DNS record type=%d", dns->wType)); + } + + *out_dnsp = dnsp; + return WERR_OK; + +fail_nomemory: + return WERR_NOT_ENOUGH_MEMORY; +} + +/* Intialize tree with given name as the root */ +static struct dns_tree *dns_tree_init(TALLOC_CTX *mem_ctx, const char *name, void *data) +{ + struct dns_tree *tree; + + tree = talloc_zero(mem_ctx, struct dns_tree); + if (tree == NULL) { + return NULL; + } + + tree->name = talloc_strdup(tree, name); + if (tree->name == NULL) { + talloc_free(tree); + return NULL; + } + + tree->data = data; + + return tree; +} + + +/* Add a child one level below */ +static struct dns_tree *dns_tree_add(struct dns_tree *tree, const char *name, void *data) +{ + struct dns_tree *node; + + node = talloc_zero(tree, struct dns_tree); + if (node == NULL) { + return NULL; + } + + node->name = talloc_strdup(tree, name); + if (node->name == NULL) { + talloc_free(node); + return NULL; + } + node->level = tree->level + 1; + node->num_children = 0; + node->children = NULL; + node->data = data; + + if (tree->num_children == 0) { + tree->children = talloc_zero(tree, struct dns_tree *); + } else { + tree->children = talloc_realloc(tree, tree->children, struct dns_tree *, + tree->num_children+1); + } + if (tree->children == NULL) { + talloc_free(node); + return NULL; + } + tree->children[tree->num_children] = node; + tree->num_children++; + + return node; +} + +/* Find a node that matches the name components */ +static struct dns_tree *dns_tree_find(struct dns_tree *tree, int ncount, char **nlist, int *match_count) +{ + struct dns_tree *node, *next; + int i, j, start; + + *match_count = -1; + + if (strcmp(tree->name, "@") == 0) { + start = 0; + } else { + if (strcasecmp(tree->name, nlist[ncount-1]) != 0) { + return NULL; + } + start = 1; + *match_count = 0; + } + + node = tree; + for (i=start; i<ncount; i++) { + if (node->num_children == 0) { + break; + } + next = NULL; + for (j=0; j<node->num_children; j++) { + if (strcasecmp(nlist[(ncount-1)-i], node->children[j]->name) == 0) { + next = node->children[j]; + *match_count = i; + break; + } + } + if (next == NULL) { + break; + } else { + node = next; + } + } + + return node; +} + +/* Build a 2-level tree for resulting dns names */ +struct dns_tree *dns_build_tree(TALLOC_CTX *mem_ctx, const char *name, struct ldb_result *res) +{ + struct dns_tree *root, *base, *tree, *node; + const char *ptr; + int rootcount, ncount; + char **nlist; + int i, level, match_count; + + rootcount = dns_split_name_components(mem_ctx, name, &nlist); + if (rootcount <= 0) { + return NULL; + } + + root = dns_tree_init(mem_ctx, nlist[rootcount-1], NULL); + if (root == NULL) { + talloc_free(nlist); + return NULL; + } + + tree = root; + for (i=rootcount-2; i>=0; i--) { + tree = dns_tree_add(tree, nlist[i], NULL); + if (tree == NULL) { + goto failed; + } + } + + base = tree; + + /* Add all names in the result in a tree */ + for (i=0; i<res->count; i++) { + ptr = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL); + if (ptr == NULL) { + DBG_ERR("dnsserver: dns record has no name (%s)", + ldb_dn_get_linearized(res->msgs[i]->dn)); + goto failed; + } + + /* + * This might be the sub-domain in the zone being + * requested, or @ for the root of the zone + */ + if (strcasecmp(ptr, name) == 0) { + base->data = res->msgs[i]; + continue; + } + + ncount = dns_split_name_components(root, ptr, &nlist); + if (ncount < 0) { + goto failed; + } + + /* Find matching node */ + tree = dns_tree_find(root, ncount, nlist, &match_count); + if (tree == NULL) { + goto failed; + } + + /* If the node is on leaf, then add record data */ + if (match_count+1 == ncount) { + tree->data = res->msgs[i]; + } + + /* Add missing name components */ + for (level=match_count+1; level<ncount; level++) { + if (tree->level == rootcount+1) { + break; + } + if (level == ncount-1) { + node = dns_tree_add(tree, nlist[(ncount-1)-level], res->msgs[i]); + } else { + node = dns_tree_add(tree, nlist[(ncount-1)-level], NULL); + } + if (node == NULL) { + goto failed; + } + tree = node; + } + + talloc_free(nlist); + } + + /* Mark the base record, so it can be found easily */ + base->level = -1; + + return root; + +failed: + talloc_free(nlist); + talloc_free(root); + return NULL; +} + + +static void _dns_add_name(TALLOC_CTX *mem_ctx, const char *name, char ***add_names, int *add_count) +{ + int i; + char **ptr = *add_names; + int count = *add_count; + + for (i=0; i<count; i++) { + if (strcasecmp(ptr[i], name) == 0) { + return; + } + } + + ptr = talloc_realloc(mem_ctx, ptr, char *, count+1); + if (ptr == NULL) { + return; + } + + ptr[count] = talloc_strdup(mem_ctx, name); + if (ptr[count] == NULL) { + talloc_free(ptr); + return; + } + + *add_names = ptr; + *add_count = count+1; +} + + +static void dns_find_additional_names(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *rec, char ***add_names, int *add_count) +{ + if (add_names == NULL) { + return; + } + + switch (rec->wType) { + + case DNS_TYPE_NS: + _dns_add_name(mem_ctx, rec->data.ns, add_names, add_count); + break; + + case DNS_TYPE_CNAME: + _dns_add_name(mem_ctx, rec->data.cname, add_names, add_count); + break; + + case DNS_TYPE_SOA: + _dns_add_name(mem_ctx, rec->data.soa.mname, add_names, add_count); + break; + + case DNS_TYPE_MX: + _dns_add_name(mem_ctx, rec->data.mx.nameTarget, add_names, add_count); + break; + + case DNS_TYPE_SRV: + _dns_add_name(mem_ctx, rec->data.srv.nameTarget, add_names, add_count); + break; + + default: + break; + } +} + + +WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx, + struct dnsserver_zone *z, + enum dns_record_type record_type, + unsigned int select_flag, + const char *branch_name, + struct ldb_message *msg, + int num_children, + struct DNS_RPC_RECORDS_ARRAY *recs, + char ***add_names, + int *add_count) +{ + struct ldb_message_element *el; + const char *ptr; + int i, j; + bool found; + + if (recs->count == 0) { + recs->rec = talloc_zero(recs, struct DNS_RPC_RECORDS); + } else { + recs->rec = talloc_realloc(recs, recs->rec, struct DNS_RPC_RECORDS, recs->count+1); + } + if (recs->rec == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + i = recs->count; + recs->rec[i].wLength = 0; + recs->rec[i].wRecordCount = 0; + recs->rec[i].dwChildCount = num_children; + recs->rec[i].dwFlags = 0; + + /* The base records returned with empty name */ + /* Children records returned with names */ + if (branch_name == NULL) { + recs->rec[i].dnsNodeName.str = talloc_strdup(recs, ""); + recs->rec[i].dnsNodeName.len = 0; + } else { + recs->rec[i].dnsNodeName.str = talloc_strdup(recs, branch_name); + recs->rec[i].dnsNodeName.len = strlen(branch_name); + } + recs->rec[i].records = talloc_zero_array(recs, struct DNS_RPC_RECORD, 0); + recs->count++; + + /* Allow empty records */ + if (msg == NULL) { + return WERR_OK; + } + + /* Do not return RR records, if the node has children */ + if (branch_name != NULL && num_children > 0) { + return WERR_OK; + } + + ptr = ldb_msg_find_attr_as_string(msg, "name", NULL); + if (ptr == NULL) { + DBG_ERR("dnsserver: dns record has no name (%s)", + ldb_dn_get_linearized(msg->dn)); + return WERR_INTERNAL_DB_ERROR; + } + + el = ldb_msg_find_element(msg, "dnsRecord"); + if (el == NULL || el->values == 0) { + return WERR_OK; + } + + /* Add RR records */ + for (j=0; j<el->num_values; j++) { + struct dnsp_DnssrvRpcRecord dnsp_rec; + struct DNS_RPC_RECORD *dns_rec; + enum ndr_err_code ndr_err; + + ndr_err = ndr_pull_struct_blob(&el->values[j], mem_ctx, &dnsp_rec, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("dnsserver: Unable to parse dns record (%s)", ldb_dn_get_linearized(msg->dn))); + return WERR_INTERNAL_DB_ERROR; + } + + /* Match the records based on search criteria */ + if (record_type == DNS_TYPE_ALL || dnsp_rec.wType == record_type) { + found = false; + + if (select_flag & DNS_RPC_VIEW_AUTHORITY_DATA) { + if (dnsp_rec.rank == DNS_RANK_ZONE) { + found = true; + } else if (dnsp_rec.rank == DNS_RANK_NS_GLUE) { + /* + * If branch_name is NULL, we're + * explicitly asked to also return + * DNS_RANK_NS_GLUE records + */ + if (branch_name == NULL) { + found = true; + } + } + } + if (select_flag & DNS_RPC_VIEW_CACHE_DATA) { + if (dnsp_rec.rank == DNS_RANK_ZONE) { + found = true; + } + } + if (select_flag & DNS_RPC_VIEW_GLUE_DATA) { + if (dnsp_rec.rank == DNS_RANK_GLUE) { + found = true; + } + } + if (select_flag & DNS_RPC_VIEW_ROOT_HINT_DATA) { + if (dnsp_rec.rank == DNS_RANK_ROOT_HINT) { + found = true; + } + } + + if (found) { + recs->rec[i].records = talloc_realloc(recs, + recs->rec[i].records, + struct DNS_RPC_RECORD, + recs->rec[i].wRecordCount+1); + if (recs->rec[i].records == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + dns_rec = &recs->rec[i].records[recs->rec[i].wRecordCount]; + dnsp_to_dns_copy(recs, &dnsp_rec, dns_rec); + + /* Fix record flags */ + if (strcmp(ptr, "@") == 0) { + dns_rec->dwFlags |= DNS_RPC_FLAG_ZONE_ROOT; + + if (dnsp_rec.rank == DNS_RANK_ZONE) { + dns_rec->dwFlags |= DNS_RPC_FLAG_AUTH_ZONE_ROOT; + } + } + + if (dns_rec->dwFlags == DNS_RANK_NS_GLUE) { + dns_rec->dwFlags |= DNS_RPC_FLAG_ZONE_ROOT; + } + + recs->rec[i].wRecordCount++; + + dns_find_additional_names(mem_ctx, &dnsp_rec, add_names, add_count); + } + } + } + + return WERR_OK; +} + + +int dns_name_compare(struct ldb_message * const *m1, struct ldb_message * const *m2, + const char *search_name) +{ + const char *name1, *name2; + const char *ptr1, *ptr2; + + name1 = ldb_msg_find_attr_as_string(*m1, "name", NULL); + name2 = ldb_msg_find_attr_as_string(*m2, "name", NULL); + if (name1 == NULL || name2 == NULL) { + return 0; + } + + /* Compare the last components of names. + * If search_name is not NULL, compare the second last components of names */ + ptr1 = strrchr(name1, '.'); + if (ptr1 == NULL) { + ptr1 = name1; + } else { + if (search_name && strcasecmp(ptr1+1, search_name) == 0) { + ptr1--; + while (ptr1 != name1) { + ptr1--; + if (*ptr1 == '.') { + break; + } + } + } + if (*ptr1 == '.') { + ptr1 = &ptr1[1]; + } + } + + ptr2 = strrchr(name2, '.'); + if (ptr2 == NULL) { + ptr2 = name2; + } else { + if (search_name && strcasecmp(ptr2+1, search_name) == 0) { + ptr2--; + while (ptr2 != name2) { + ptr2--; + if (*ptr2 == '.') { + break; + } + } + } + if (*ptr2 == '.') { + ptr2 = &ptr2[1]; + } + } + + return strcasecmp(ptr1, ptr2); +} diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c new file mode 100644 index 0000000..bde54a0 --- /dev/null +++ b/source4/rpc_server/dnsserver/dnsdb.c @@ -0,0 +1,1272 @@ +/* + 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" +#include "librpc/gen_ndr/ndr_security.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "dsdb/samdb/samdb.h" +#include "libcli/security/security.h" +#include "dsdb/common/util.h" + +#undef strcasecmp + +/* There are only 2 fixed partitions for DNS */ +struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx, + struct dnsserver_serverinfo *serverinfo, + struct ldb_context *samdb) +{ + struct dnsserver_partition *partitions, *p; + + partitions = NULL; + + /* Domain partition */ + p = talloc_zero(mem_ctx, struct dnsserver_partition); + if (p == NULL) { + goto failed; + } + + p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszDomainDirectoryPartition); + if (p->partition_dn == NULL) { + goto failed; + } + + p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn); + p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_DOMAIN_DEFAULT | DNS_DP_ENLISTED; + p->is_forest = false; + + DLIST_ADD_END(partitions, p); + + /* Forest Partition */ + p = talloc_zero(mem_ctx, struct dnsserver_partition); + if (p == NULL) { + goto failed; + } + + p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszForestDirectoryPartition); + if (p->partition_dn == NULL) { + goto failed; + } + + p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn); + p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_FOREST_DEFAULT | DNS_DP_ENLISTED; + p->is_forest = true; + + DLIST_ADD_END(partitions, p); + + return partitions; + +failed: + return NULL; + +} + + +/* Search for all dnsZone records */ +struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_partition *p) +{ + TALLOC_CTX *tmp_ctx; + const char * const attrs[] = {"name", "dNSProperty", NULL}; + struct ldb_dn *dn; + struct ldb_result *res; + struct dnsserver_zone *zones, *z; + int i, j, ret; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NULL; + } + + dn = ldb_dn_copy(tmp_ctx, p->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)"); + 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++) { + char *name; + struct ldb_message_element *element = NULL; + struct dnsp_DnsProperty *props = NULL; + enum ndr_err_code err; + z = talloc_zero(mem_ctx, struct dnsserver_zone); + if (z == NULL) { + goto failed; + } + + z->partition = p; + name = talloc_strdup(z, + ldb_msg_find_attr_as_string(res->msgs[i], + "name", NULL)); + if (strcmp(name, "..TrustAnchors") == 0) { + talloc_free(z); + continue; + } + if (strcmp(name, "RootDNSServers") == 0) { + talloc_free(name); + z->name = talloc_strdup(z, "."); + } else { + z->name = name; + } + z->zone_dn = talloc_steal(z, res->msgs[i]->dn); + + DLIST_ADD_END(zones, z); + DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name)); + + element = ldb_msg_find_element(res->msgs[i], "dNSProperty"); + if(element != NULL){ + props = talloc_zero_array(tmp_ctx, + struct dnsp_DnsProperty, + element->num_values); + for (j = 0; j < element->num_values; j++ ) { + err = ndr_pull_struct_blob( + &(element->values[j]), + mem_ctx, + &props[j], + (ndr_pull_flags_fn_t) + ndr_pull_dnsp_DnsProperty); + if (!NDR_ERR_CODE_IS_SUCCESS(err)){ + /* + * Per Microsoft we must + * ignore invalid data here + * and continue as a Windows + * server can put in a + * structure with an invalid + * length. + * + * We can safely fill in an + * extra empty property here + * because + * dns_zoneinfo_load_zone_property() + * just ignores + * DSPROPERTY_ZONE_EMPTY + */ + ZERO_STRUCT(props[j]); + props[j].id = DSPROPERTY_ZONE_EMPTY; + continue; + } + } + z->tmp_props = props; + z->num_props = element->num_values; + } + } + return zones; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + + +/* Find DNS partition information */ +struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_partition *p) +{ + const char * const attrs[] = { "instanceType", "msDs-masteredBy", NULL }; + const char * const attrs_none[] = { NULL }; + struct ldb_result *res; + struct ldb_message_element *el; + struct ldb_dn *dn; + struct dnsserver_partition_info *partinfo; + int i, ret, instance_type; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NULL; + } + + partinfo = talloc_zero(mem_ctx, struct dnsserver_partition_info); + if (partinfo == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + + /* Search for the active replica and state */ + ret = ldb_search(samdb, tmp_ctx, &res, p->partition_dn, LDB_SCOPE_BASE, + attrs, NULL); + if (ret != LDB_SUCCESS || res->count != 1) { + goto failed; + } + + /* Set the state of the partition */ + instance_type = ldb_msg_find_attr_as_int(res->msgs[0], "instanceType", -1); + if (instance_type == -1) { + partinfo->dwState = DNS_DP_STATE_UNKNOWN; + } else if (instance_type & INSTANCE_TYPE_NC_COMING) { + partinfo->dwState = DNS_DP_STATE_REPL_INCOMING; + } else if (instance_type & INSTANCE_TYPE_NC_GOING) { + partinfo->dwState = DNS_DP_STATE_REPL_OUTGOING; + } else { + partinfo->dwState = DNS_DP_OKAY; + } + + el = ldb_msg_find_element(res->msgs[0], "msDs-masteredBy"); + if (el == NULL) { + partinfo->dwReplicaCount = 0; + partinfo->ReplicaArray = NULL; + } else { + partinfo->dwReplicaCount = el->num_values; + partinfo->ReplicaArray = talloc_zero_array(partinfo, + struct DNS_RPC_DP_REPLICA *, + el->num_values); + if (partinfo->ReplicaArray == NULL) { + goto failed; + } + for (i=0; i<el->num_values; i++) { + partinfo->ReplicaArray[i] = talloc_zero(partinfo, + struct DNS_RPC_DP_REPLICA); + if (partinfo->ReplicaArray[i] == NULL) { + goto failed; + } + partinfo->ReplicaArray[i]->pszReplicaDn = talloc_strdup( + partinfo, + (const char *)el->values[i].data); + if (partinfo->ReplicaArray[i]->pszReplicaDn == NULL) { + goto failed; + } + } + } + talloc_free(res); + + /* Search for cross-reference object */ + dn = ldb_dn_copy(tmp_ctx, ldb_get_config_basedn(samdb)); + if (dn == NULL) { + goto failed; + } + + ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_DEFAULT, attrs_none, + "(nCName=%s)", ldb_dn_get_linearized(p->partition_dn)); + if (ret != LDB_SUCCESS || res->count != 1) { + goto failed; + } + partinfo->pszCrDn = talloc_strdup(partinfo, ldb_dn_get_linearized(res->msgs[0]->dn)); + if (partinfo->pszCrDn == NULL) { + goto failed; + } + talloc_free(res); + + talloc_free(tmp_ctx); + return partinfo; + +failed: + talloc_free(tmp_ctx); + talloc_free(partinfo); + return NULL; +} + + +/* Increment serial number and update timestamp */ +static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + WERROR *werr) +{ + 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; + + *werr = WERR_INTERNAL_DB_ERROR; + + 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 = 0; + 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)) { + *werr = WERR_NOT_ENOUGH_MEMORY; + return -1; + } + break; + } + } + + if (serial != -1) { + el->flags = LDB_FLAG_MOD_REPLACE; + ret = ldb_modify(samdb, res->msgs[0]); + if (ret != LDB_SUCCESS) { + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + *werr = WERR_ACCESS_DENIED; + } + return -1; + } + } + + *werr = WERR_OK; + + return serial; +} + + +/* Add DNS record to the database */ +static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct ldb_dn *dn, + int num_rec, + struct dnsp_DnssrvRpcRecord *rec) +{ + struct ldb_message *msg; + struct ldb_val v; + int ret; + enum ndr_err_code ndr_err; + int i; + + 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_NOT_ENOUGH_MEMORY; + } + + if (num_rec > 0 && rec) { + for (i=0; i<num_rec; i++) { + ndr_err = ndr_push_struct_blob(&v, mem_ctx, &rec[i], + (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GEN_FAILURE; + } + + ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL); + if (ret != LDB_SUCCESS) { + return WERR_NOT_ENOUGH_MEMORY; + } + } + } + + ret = ldb_add(samdb, msg); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} + + +/* Add dnsNode record to the database with DNS record */ +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; + char *encoded_name = ldb_binary_encode_string(mem_ctx, name); + struct ldb_val name_val = data_blob_string_const(name); + int ret; + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs, + "(&(objectClass=dnsNode)(name=%s))", + encoded_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_val(dn, "DC", name_val)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 0, NULL); +} + +static void set_record_rank(struct dnsserver_zone *z, + const char *name, + struct dnsp_DnssrvRpcRecord *rec) +{ + if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) { + if (strcmp(name, "@") != 0 && rec->wType == DNS_TYPE_NS) { + rec->rank = DNS_RANK_NS_GLUE; + } else { + rec->rank = DNS_RANK_ZONE; + } + } else if (strcmp(z->name, ".") == 0) { + rec->rank = DNS_RANK_ROOT_HINT; + } +} + + +/* Add a DNS record */ +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", "dNSTombstoned", NULL }; + struct ldb_result *res; + struct dnsp_DnssrvRpcRecord *rec = NULL; + struct ldb_message_element *el; + struct ldb_dn *dn; + enum ndr_err_code ndr_err; + int ret, i; + int serial; + WERROR werr; + bool was_tombstoned = false; + char *encoded_name = ldb_binary_encode_string(mem_ctx, name); + + werr = dns_to_dnsp_convert(mem_ctx, add_record, &rec, true); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + /* Set the correct rank for the record. */ + set_record_rank(z, name, rec); + + serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr); + if (serial < 0) { + return werr; + } + + rec->dwSerial = serial; + rec->dwTimeStamp = 0; + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s))", + encoded_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, 1, 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_NOT_ENOUGH_MEMORY; + } + } + + was_tombstoned = ldb_msg_find_attr_as_bool(res->msgs[0], + "dNSTombstoned", false); + if (was_tombstoned) { + el->num_values = 0; + } + + 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_GEN_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_GEN_FAILURE; + } + + el->flags = LDB_FLAG_MOD_REPLACE; + + el = ldb_msg_find_element(res->msgs[0], "dNSTombstoned"); + if (el != NULL) { + el->flags = LDB_FLAG_MOD_DELETE; + } + + ret = ldb_modify(samdb, res->msgs[0]); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} + + +/* Update a DNS record */ +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 rec2; + struct dnsp_DnssrvRpcRecord *arec = NULL, *drec = NULL; + struct ldb_message_element *el; + enum ndr_err_code ndr_err; + int ret, i; + int serial; + WERROR werr; + bool updating_ttl = false; + char *encoded_name = ldb_binary_encode_string(mem_ctx, name); + + werr = dns_to_dnsp_convert(mem_ctx, add_record, &arec, true); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + werr = dns_to_dnsp_convert(mem_ctx, del_record, &drec, true); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))", + encoded_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++) { + 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_GEN_FAILURE; + } + + if (dns_record_match(arec, &rec2)) { + break; + } + } + if (i < el->num_values) { + /* + * The record already exists, which is an error UNLESS we are + * doing an in-place update. + * + * Therefore we need to see if drec also matches, in which + * case it's OK, though we can only update dwTtlSeconds and + * reset the timestamp to zero. + */ + updating_ttl = dns_record_match(drec, &rec2); + if (! updating_ttl) { + return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS; + } + /* In this case the next loop is redundant */ + } + + for (i=0; i<el->num_values; i++) { + 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_GEN_FAILURE; + } + + if (dns_record_match(drec, &rec2)) { + /* + * we are replacing this one with arec, which is done + * by pushing arec into el->values[i] below, after the + * various manipulations. + */ + break; + } + } + if (i == el->num_values) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + + /* + * If we're updating a SOA record, use the specified serial. + * + * Otherwise, if we are updating ttl in place (i.e., not changing + * .wType and .data on a record), we should increment the existing + * serial, and save to the SOA. + * + * Outside of those two cases, we look for the zone's SOA record and + * use its serial. + */ + if (arec->wType != DNS_TYPE_SOA) { + if (updating_ttl) { + /* + * In this case, we keep some of the old values. + */ + arec->dwSerial = rec2.dwSerial; + arec->dwReserved = rec2.dwReserved; + /* + * TODO: if the old TTL and the new TTL are + * different, the serial number is incremented. + */ + } else { + arec->dwReserved = 0; + serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr); + if (serial < 0) { + return werr; + } + arec->dwSerial = serial; + } + } + + /* Set the correct rank for the record. */ + set_record_rank(z, name, arec); + /* + * Successful RPC updates *always* zero timestamp and flags and set + * version. + */ + arec->dwTimeStamp = 0; + arec->version = 5; + arec->flags = 0; + + 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_GEN_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; +} + + +/* Delete a DNS record */ +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 = NULL; + struct ldb_message_element *el; + enum ndr_err_code ndr_err; + int ret, i; + int serial; + WERROR werr; + + serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr); + if (serial < 0) { + return werr; + } + + werr = dns_to_dnsp_convert(mem_ctx, del_record, &rec, false); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs, + "(&(objectClass=dnsNode)(name=%s))", + ldb_binary_encode_string(mem_ctx, name)); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + if (res->count == 0) { + return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST; + } + if (res->count > 1) { + return WERR_DNS_ERROR_RCODE_SERVER_FAILURE; + } + + 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_GEN_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; +} + + +static bool dnsserver_db_msg_add_dnsproperty(TALLOC_CTX *mem_ctx, + struct ldb_message *msg, + struct dnsp_DnsProperty *prop) +{ + DATA_BLOB *prop_blob; + enum ndr_err_code ndr_err; + int ret; + + prop_blob = talloc_zero(mem_ctx, DATA_BLOB); + if (prop_blob == NULL) return false; + + ndr_err = ndr_push_struct_blob(prop_blob, mem_ctx, prop, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + ret = ldb_msg_add_steal_value(msg, "dNSProperty", prop_blob); + if (ret != LDB_SUCCESS) { + return false; + } + return true; +} + +WERROR dnsserver_db_do_reset_dword(struct ldb_context *samdb, + struct dnsserver_zone *z, + struct DNS_RPC_NAME_AND_PARAM *n_p) +{ + struct ldb_message_element *element = NULL; + struct dnsp_DnsProperty *prop = NULL; + enum ndr_err_code err; + TALLOC_CTX *tmp_ctx = NULL; + const char * const attrs[] = {"dNSProperty", NULL}; + struct ldb_result *res = NULL; + int i, ret, prop_id; + + if (strcasecmp(n_p->pszNodeName, "Aging") == 0) { + z->zoneinfo->fAging = n_p->dwParam; + prop_id = DSPROPERTY_ZONE_AGING_STATE; + } else if (strcasecmp(n_p->pszNodeName, "RefreshInterval") == 0) { + z->zoneinfo->dwRefreshInterval = n_p->dwParam; + prop_id = DSPROPERTY_ZONE_REFRESH_INTERVAL; + } else if (strcasecmp(n_p->pszNodeName, "NoRefreshInterval") == 0) { + z->zoneinfo->dwNoRefreshInterval = n_p->dwParam; + prop_id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL; + } else if (strcasecmp(n_p->pszNodeName, "AllowUpdate") == 0) { + z->zoneinfo->fAllowUpdate = n_p->dwParam; + prop_id = DSPROPERTY_ZONE_ALLOW_UPDATE; + } else { + return WERR_UNKNOWN_PROPERTY; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + ret = ldb_search(samdb, tmp_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, + attrs, "(objectClass=dnsZone)"); + if (ret != LDB_SUCCESS) { + DBG_ERR("dnsserver: no zone: %s\n", + ldb_dn_get_linearized(z->zone_dn)); + TALLOC_FREE(tmp_ctx); + return WERR_INTERNAL_DB_ERROR; + } + + if (res->count != 1) { + DBG_ERR("dnsserver: duplicate zone: %s\n", + ldb_dn_get_linearized(z->zone_dn)); + TALLOC_FREE(tmp_ctx); + return WERR_GEN_FAILURE; + } + + element = ldb_msg_find_element(res->msgs[0], "dNSProperty"); + if (element == NULL) { + DBG_ERR("dnsserver: zone %s has no properties.\n", + ldb_dn_get_linearized(z->zone_dn)); + TALLOC_FREE(tmp_ctx); + return WERR_INTERNAL_DB_ERROR; + } + + for (i = 0; i < element->num_values; i++) { + prop = talloc_zero(element, struct dnsp_DnsProperty); + if (prop == NULL) { + TALLOC_FREE(tmp_ctx); + return WERR_NOT_ENOUGH_MEMORY; + } + err = ndr_pull_struct_blob( + &(element->values[i]), + tmp_ctx, + prop, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty); + if (!NDR_ERR_CODE_IS_SUCCESS(err)){ + /* + * If we can't pull it then try again parsing + * it again. A Windows server in the domain + * will permit the addition of an invalidly + * formed property with a 0 length and cause a + * failure here + */ + struct dnsp_DnsProperty_short + *short_property + = talloc_zero(element, + struct dnsp_DnsProperty_short); + if (short_property == NULL) { + TALLOC_FREE(tmp_ctx); + return WERR_NOT_ENOUGH_MEMORY; + } + err = ndr_pull_struct_blob_all( + &(element->values[i]), + tmp_ctx, + short_property, + (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty_short); + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + /* + * Unknown invalid data should be + * ignored and logged to match Windows + * behaviour + */ + DBG_NOTICE("dnsserver: couldn't PULL " + "dnsProperty value#%d in " + "zone %s while trying to " + "reset id %d\n", + i, + ldb_dn_get_linearized(z->zone_dn), + prop_id); + continue; + } + + /* + * Initialise the parts of the property not + * overwritten by value() in the IDL for + * re-push + */ + *prop = (struct dnsp_DnsProperty){ + .namelength = short_property->namelength, + .id = short_property->id, + .name = short_property->name + /* .data will be filled in below */ + }; + } + + if (prop->id == prop_id) { + switch (prop_id) { + case DSPROPERTY_ZONE_AGING_STATE: + prop->data.aging_enabled = n_p->dwParam; + break; + case DSPROPERTY_ZONE_NOREFRESH_INTERVAL: + prop->data.norefresh_hours = n_p->dwParam; + break; + case DSPROPERTY_ZONE_REFRESH_INTERVAL: + prop->data.refresh_hours = n_p->dwParam; + break; + case DSPROPERTY_ZONE_ALLOW_UPDATE: + prop->data.allow_update_flag = n_p->dwParam; + break; + } + + err = ndr_push_struct_blob( + &(element->values[i]), + tmp_ctx, + prop, + (ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty); + if (!NDR_ERR_CODE_IS_SUCCESS(err)){ + DBG_ERR("dnsserver: couldn't PUSH dns prop id " + "%d in zone %s\n", + prop->id, + ldb_dn_get_linearized(z->zone_dn)); + TALLOC_FREE(tmp_ctx); + return WERR_INTERNAL_DB_ERROR; + } + } + } + + element->flags = LDB_FLAG_MOD_REPLACE; + ret = ldb_modify(samdb, res->msgs[0]); + if (ret != LDB_SUCCESS) { + TALLOC_FREE(tmp_ctx); + DBG_ERR("dnsserver: Failed to modify zone %s prop %s: %s\n", + z->name, + n_p->pszNodeName, + ldb_errstring(samdb)); + return WERR_INTERNAL_DB_ERROR; + } + TALLOC_FREE(tmp_ctx); + + return WERR_OK; +} + +/* Create dnsZone record to database and set security descriptor */ +static WERROR dnsserver_db_do_create_zone(TALLOC_CTX *tmp_ctx, + struct ldb_context *samdb, + struct ldb_dn *zone_dn, + struct dnsserver_zone *z) +{ + const char * const attrs[] = { "objectSID", NULL }; + struct ldb_message *msg; + struct ldb_result *res; + struct ldb_message_element *el; + const char sddl_template[] = "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;CC;;;AU)(A;;RPLCLORC;;;WD)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)(A;CIID;LC;;;RU)(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)S:AI"; + char *sddl; + struct dom_sid dnsadmins_sid; + const struct dom_sid *domain_sid; + struct security_descriptor *secdesc; + struct dnsp_DnsProperty *prop; + DATA_BLOB *sd_encoded; + enum ndr_err_code ndr_err; + int ret; + + /* Get DnsAdmins SID */ + ret = ldb_search(samdb, tmp_ctx, &res, ldb_get_default_basedn(samdb), + LDB_SCOPE_DEFAULT, attrs, "(sAMAccountName=DnsAdmins)"); + if (ret != LDB_SUCCESS || res->count != 1) { + return WERR_INTERNAL_DB_ERROR; + } + + el = ldb_msg_find_element(res->msgs[0], "objectSID"); + if (el == NULL || el->num_values != 1) { + return WERR_INTERNAL_DB_ERROR; + } + + ndr_err = ndr_pull_struct_blob(&el->values[0], tmp_ctx, &dnsadmins_sid, + (ndr_pull_flags_fn_t)ndr_pull_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INTERNAL_DB_ERROR; + } + + /* create security descriptor with DnsAdmins GUID in sddl template */ + sddl = talloc_asprintf(tmp_ctx, sddl_template, + dom_sid_string(tmp_ctx, &dnsadmins_sid)); + if (sddl == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + talloc_free(res); + + domain_sid = samdb_domain_sid(samdb); + if (domain_sid == NULL) { + return WERR_INTERNAL_DB_ERROR; + } + + secdesc = sddl_decode(tmp_ctx, sddl, domain_sid); + if (secdesc == NULL) { + return WERR_GEN_FAILURE; + } + + msg = ldb_msg_new(tmp_ctx); + W_ERROR_HAVE_NO_MEMORY(msg); + + msg->dn = zone_dn; + ret = ldb_msg_add_string(msg, "objectClass", "dnsZone"); + if (ret != LDB_SUCCESS) { + return WERR_NOT_ENOUGH_MEMORY; + } + + sd_encoded = talloc_zero(tmp_ctx, DATA_BLOB); + W_ERROR_HAVE_NO_MEMORY(sd_encoded); + + ndr_err = ndr_push_struct_blob(sd_encoded, tmp_ctx, secdesc, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_GEN_FAILURE; + } + + ret = ldb_msg_add_steal_value(msg, "nTSecurityDescriptor", sd_encoded); + if (ret != LDB_SUCCESS) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* dns zone Properties */ + prop = talloc_zero(tmp_ctx, struct dnsp_DnsProperty); + W_ERROR_HAVE_NO_MEMORY(prop); + + prop->version = 1; + + /* zone type */ + prop->id = DSPROPERTY_ZONE_TYPE; + prop->data.zone_type = z->zoneinfo->dwZoneType; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* allow update */ + prop->id = DSPROPERTY_ZONE_ALLOW_UPDATE; + prop->data.allow_update_flag = z->zoneinfo->fAllowUpdate; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* secure time */ + prop->id = DSPROPERTY_ZONE_SECURE_TIME; + prop->data.zone_secure_time = 0; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* norefresh interval */ + prop->id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL; + prop->data.norefresh_hours = 168; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* refresh interval */ + prop->id = DSPROPERTY_ZONE_REFRESH_INTERVAL; + prop->data.refresh_hours = 168; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* aging state */ + prop->id = DSPROPERTY_ZONE_AGING_STATE; + prop->data.aging_enabled = z->zoneinfo->fAging; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* aging enabled time */ + prop->id = DSPROPERTY_ZONE_AGING_ENABLED_TIME; + prop->data.next_scavenging_cycle_hours = 0; + if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) { + return WERR_NOT_ENOUGH_MEMORY; + } + + talloc_free(prop); + + ret = ldb_add(samdb, msg); + if (ret != LDB_SUCCESS) { + DEBUG(0, ("dnsserver: Failed to create zone (%s): %s\n", + z->name, ldb_errstring(samdb))); + + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + return WERR_ACCESS_DENIED; + } + + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} + + +/* Create new dnsZone record and @ record (SOA + NS) */ +WERROR dnsserver_db_create_zone(struct ldb_context *samdb, + struct dnsserver_partition *partitions, + struct dnsserver_zone *zone, + struct loadparm_context *lp_ctx) +{ + struct dnsserver_partition *p; + bool in_forest = false; + WERROR status; + struct ldb_dn *dn; + TALLOC_CTX *tmp_ctx; + struct dnsp_DnssrvRpcRecord *dns_rec; + struct dnsp_soa soa; + char *tmpstr, *server_fqdn, *soa_email; + struct ldb_val name_val = data_blob_string_const(zone->name); + + /* We only support primary zones for now */ + if (zone->zoneinfo->dwZoneType != DNS_ZONE_TYPE_PRIMARY) { + return WERR_CALL_NOT_IMPLEMENTED; + } + + /* Get the correct partition */ + if (zone->partition->dwDpFlags & DNS_DP_FOREST_DEFAULT) { + in_forest = true; + } + for (p = partitions; p; p = p->next) { + if (in_forest == p->is_forest) { + break; + } + } + if (p == NULL) { + return WERR_DNS_ERROR_DP_DOES_NOT_EXIST; + } + + tmp_ctx = talloc_new(NULL); + W_ERROR_HAVE_NO_MEMORY(tmp_ctx); + + dn = ldb_dn_copy(tmp_ctx, p->partition_dn); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(dn, tmp_ctx); + + if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) { + talloc_free(tmp_ctx); + return WERR_NOT_ENOUGH_MEMORY; + } + + if (!ldb_dn_add_child_val(dn, "DC", name_val)) { + talloc_free(tmp_ctx); + return WERR_NOT_ENOUGH_MEMORY; + } + + /* Add dnsZone record */ + status = dnsserver_db_do_create_zone(tmp_ctx, samdb, dn, zone); + if (!W_ERROR_IS_OK(status)) { + talloc_free(tmp_ctx); + return status; + } + + if (!ldb_dn_add_child_fmt(dn, "DC=@")) { + talloc_free(tmp_ctx); + return WERR_NOT_ENOUGH_MEMORY; + } + + dns_rec = talloc_zero_array(tmp_ctx, struct dnsp_DnssrvRpcRecord, 2); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(dns_rec, tmp_ctx); + + tmpstr = talloc_asprintf(tmp_ctx, "%s.%s", + lpcfg_netbios_name(lp_ctx), + lpcfg_realm(lp_ctx)); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx); + server_fqdn = strlower_talloc(tmp_ctx, tmpstr); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(server_fqdn, tmp_ctx); + talloc_free(tmpstr); + + tmpstr = talloc_asprintf(tmp_ctx, "hostmaster.%s", + lpcfg_realm(lp_ctx)); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx); + soa_email = strlower_talloc(tmp_ctx, tmpstr); + W_ERROR_HAVE_NO_MEMORY_AND_FREE(soa_email, tmp_ctx); + talloc_free(tmpstr); + + /* SOA Record - values same as defined in provision/sambadns.py */ + soa.serial = 1; + soa.refresh = 900; + soa.retry = 600; + soa.expire = 86400; + soa.minimum = 3600; + soa.mname = server_fqdn; + soa.rname = soa_email; + + dns_rec[0].wType = DNS_TYPE_SOA; + dns_rec[0].rank = DNS_RANK_ZONE; + dns_rec[0].dwSerial = soa.serial; + dns_rec[0].dwTtlSeconds = 3600; + dns_rec[0].dwTimeStamp = 0; + dns_rec[0].data.soa = soa; + + /* NS Record */ + dns_rec[1].wType = DNS_TYPE_NS; + dns_rec[1].rank = DNS_RANK_ZONE; + dns_rec[1].dwSerial = soa.serial; + dns_rec[1].dwTtlSeconds = 3600; + dns_rec[1].dwTimeStamp = 0; + dns_rec[1].data.ns = server_fqdn; + + /* Add @ Record */ + status = dnsserver_db_do_add_rec(tmp_ctx, samdb, dn, 2, dns_rec); + + talloc_free(tmp_ctx); + return status; +} + + +/* Delete dnsZone record and all DNS records in the zone */ +WERROR dnsserver_db_delete_zone(struct ldb_context *samdb, + struct dnsserver_zone *zone) +{ + int ret; + + ret = ldb_transaction_start(samdb); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + ret = dsdb_delete(samdb, zone->zone_dn, DSDB_TREE_DELETE); + if (ret != LDB_SUCCESS) { + ldb_transaction_cancel(samdb); + + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + return WERR_ACCESS_DENIED; + } + return WERR_INTERNAL_DB_ERROR; + } + + ret = ldb_transaction_commit(samdb); + if (ret != LDB_SUCCESS) { + return WERR_INTERNAL_DB_ERROR; + } + + return WERR_OK; +} diff --git a/source4/rpc_server/dnsserver/dnsserver.h b/source4/rpc_server/dnsserver/dnsserver.h new file mode 100644 index 0000000..2e46e7c --- /dev/null +++ b/source4/rpc_server/dnsserver/dnsserver.h @@ -0,0 +1,264 @@ +/* + 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/>. +*/ + +#ifndef __DNSSERVER_H__ +#define __DNSSERVER_H__ + +#include "librpc/gen_ndr/dnsp.h" +#include "librpc/gen_ndr/dnsserver.h" +#include "param/param.h" +#include "ldb.h" + +struct dnsserver_serverinfo { + uint32_t dwVersion; + uint8_t fBootMethod; + uint8_t fAdminConfigured; + uint8_t fAllowUpdate; + uint8_t fDsAvailable; + + char * pszServerName; + char * pszDsContainer; + + uint32_t dwDsForestVersion; + uint32_t dwDsDomainVersion; + uint32_t dwDsDsaVersion; + uint32_t fReadOnlyDC; + char * pszDomainName; + char * pszForestName; + char * pszDomainDirectoryPartition; + char * pszForestDirectoryPartition; + + struct DNS_ADDR_ARRAY * aipServerAddrs; + struct DNS_ADDR_ARRAY * aipListenAddrs; + struct IP4_ARRAY * aipForwarders; + + struct IP4_ARRAY * aipLogFilter; + char * pwszLogFilePath; + + uint32_t dwLogLevel; + uint32_t dwDebugLevel; + uint32_t dwEventLogLevel; + uint32_t dwLogFileMaxSize; + + uint32_t dwForwardTimeout; + uint32_t dwRpcProtocol; + uint32_t dwNameCheckFlag; + uint32_t cAddressAnswerLimit; + uint32_t dwRecursionRetry; + uint32_t dwRecursionTimeout; + uint32_t dwMaxCacheTtl; + uint32_t dwDsPollingInterval; + uint32_t dwLocalNetPriorityNetMask; + + uint32_t dwScavengingInterval; + uint32_t dwDefaultRefreshInterval; + uint32_t dwDefaultNoRefreshInterval; + uint32_t dwLastScavengeTime; + + uint8_t fAutoReverseZones; + uint8_t fAutoCacheUpdate; + + uint8_t fRecurseAfterForwarding; + uint8_t fForwardDelegations; + uint8_t fNoRecursion; + uint8_t fSecureResponses; + + uint8_t fRoundRobin; + uint8_t fLocalNetPriority; + + uint8_t fBindSecondaries; + uint8_t fWriteAuthorityNs; + + uint8_t fStrictFileParsing; + uint8_t fLooseWildcarding; + uint8_t fDefaultAgingState; +}; + +struct dnsserver_zoneinfo { + uint8_t Version; + uint32_t Flags; + uint8_t dwZoneType; + uint8_t fReverse; + uint8_t fAllowUpdate; + uint8_t fPaused; + uint8_t fShutdown; + uint8_t fAutoCreated; + + uint8_t fUseDatabase; + char * pszDataFile; + + struct IP4_ARRAY * aipMasters; + + uint32_t fSecureSecondaries; + uint32_t fNotifyLevel; + struct IP4_ARRAY * aipSecondaries; + struct IP4_ARRAY * aipNotify; + + uint32_t fUseWins; + uint32_t fUseNbstat; + + uint32_t fAging; + uint32_t dwNoRefreshInterval; + uint32_t dwRefreshInterval; + uint32_t dwAvailForScavengeTime; + struct IP4_ARRAY * aipScavengeServers; + + uint32_t dwForwarderTimeout; + uint32_t fForwarderSlave; + + struct IP4_ARRAY * aipLocalMasters; + + char * pwszZoneDn; + + uint32_t dwLastSuccessfulSoaCheck; + uint32_t dwLastSuccessfulXfr; + + uint32_t fQueuedForBackgroundLoad; + uint32_t fBackgroundLoadInProgress; + uint8_t fReadOnlyZone; + + uint32_t dwLastXfrAttempt; + uint32_t dwLastXfrResult; +}; + + +struct dnsserver_partition { + struct dnsserver_partition *prev, *next; + struct ldb_dn *partition_dn; + const char *pszDpFqdn; + uint32_t dwDpFlags; + bool is_forest; + int zones_count; +}; + + +struct dnsserver_partition_info { + const char *pszCrDn; + uint32_t dwState; + uint32_t dwReplicaCount; + struct DNS_RPC_DP_REPLICA **ReplicaArray; +}; + + +struct dnsserver_zone { + struct dnsserver_zone *prev, *next; + struct dnsserver_partition *partition; + const char *name; + struct ldb_dn *zone_dn; + struct dnsserver_zoneinfo *zoneinfo; + struct dnsp_DnsProperty *tmp_props; + int32_t num_props; +}; + + +struct dns_tree { + const char *name; + int level; + unsigned int num_children; + struct dns_tree **children; + void *data; +}; + +/* Data structure manipulation functions from dnsdata.c */ + +struct IP4_ARRAY *ip4_array_copy(TALLOC_CTX *mem_ctx, struct IP4_ARRAY *ip4); +struct DNS_ADDR_ARRAY *ip4_array_to_dns_addr_array(TALLOC_CTX *mem_ctx, struct IP4_ARRAY *ip4); +struct IP4_ARRAY *dns_addr_array_to_ip4_array(TALLOC_CTX *mem_ctx, + struct DNS_ADDR_ARRAY *ip); +struct DNS_ADDR_ARRAY *dns_addr_array_copy(TALLOC_CTX *mem_ctx, struct DNS_ADDR_ARRAY *addr); + +int dns_split_name_components(TALLOC_CTX *mem_ctx, const char *name, char ***components); +char *dns_split_node_name(TALLOC_CTX *mem_ctx, const char *node_name, const char *zone_name); + +int dns_name_compare(struct ldb_message * const *m1, struct ldb_message * const *m2, + const char *search_name); +bool dns_record_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2); + +void dnsp_to_dns_copy(TALLOC_CTX *mem_ctx, struct dnsp_DnssrvRpcRecord *dnsp, + struct DNS_RPC_RECORD *dns); +WERROR dns_to_dnsp_convert(TALLOC_CTX *mem_ctx, struct DNS_RPC_RECORD *dns, + struct dnsp_DnssrvRpcRecord **out_dnsp, + bool check_name); + +struct dns_tree *dns_build_tree(TALLOC_CTX *mem_ctx, const char *name, struct ldb_result *res); +WERROR dns_fill_records_array(TALLOC_CTX *mem_ctx, struct dnsserver_zone *z, + enum dns_record_type record_type, + unsigned int select_flag, const char *zone_name, + struct ldb_message *msg, int num_children, + struct DNS_RPC_RECORDS_ARRAY *recs, + char ***add_names, int *add_count); + + +/* Utility functions from dnsutils.c */ + +struct dnsserver_serverinfo *dnsserver_init_serverinfo(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct ldb_context *samdb); +struct dnsserver_zoneinfo *dnsserver_init_zoneinfo(struct dnsserver_zone *zone, + struct dnsserver_serverinfo *serverinfo); +struct dnsserver_zone *dnsserver_find_zone(struct dnsserver_zone *zones, + const char *zone_name); +struct ldb_dn *dnsserver_name_to_dn(TALLOC_CTX *mem_ctx, struct dnsserver_zone *z, + const char *name); +uint32_t dnsserver_zone_to_request_filter(const char *zone); + +/* Database functions from dnsdb.c */ + +struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx, + struct dnsserver_serverinfo *serverinfo, + struct ldb_context *samdb); +struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_partition *p); +struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_partition *p); +WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *node_name); +WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *node_name, + struct DNS_RPC_RECORD *add_record); +WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *node_name, + struct DNS_RPC_RECORD *add_record, + struct DNS_RPC_RECORD *del_record); +WERROR dnsserver_db_do_reset_dword(struct ldb_context *samdb, + struct dnsserver_zone *z, + struct DNS_RPC_NAME_AND_PARAM *n_p); +WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dnsserver_zone *z, + const char *node_name, + struct DNS_RPC_RECORD *del_record); +WERROR dnsserver_db_create_zone(struct ldb_context *samdb, + struct dnsserver_partition *partitions, + struct dnsserver_zone *z, + struct loadparm_context *lp_ctx); +WERROR dnsserver_db_delete_zone(struct ldb_context *samdb, + struct dnsserver_zone *z); + +#endif /* __DNSSERVER_H__ */ diff --git a/source4/rpc_server/dnsserver/dnsutils.c b/source4/rpc_server/dnsserver/dnsutils.c new file mode 100644 index 0000000..2c56946 --- /dev/null +++ b/source4/rpc_server/dnsserver/dnsutils.c @@ -0,0 +1,414 @@ +/* + 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 "rpc_server/common/common.h" +#include "dns_server/dnsserver_common.h" +#include "dsdb/samdb/samdb.h" +#include "lib/socket/netif.h" +#include "lib/util/util_net.h" +#include "dnsserver_common.h" + +#undef strcasecmp + +static struct DNS_ADDR_ARRAY *fill_dns_addr_array(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + bool listen_only) +{ + struct interface *ifaces; + int num_interfaces, i; + struct DNS_ADDR_ARRAY *dns_addr_array; + const char *ipstr; + bool have_ipv4, have_ipv6; + uint16_t family; + + have_ipv4 = have_ipv6 = false; + + if (!listen_only) { + /* + Return all interfaces from kernel + Not implemented! + */ + return NULL; + } + + /* Only the used interfaces */ + load_interface_list(mem_ctx, lp_ctx, &ifaces); + num_interfaces = iface_list_count(ifaces); + + dns_addr_array = talloc_zero(mem_ctx, struct DNS_ADDR_ARRAY); + if (dns_addr_array == NULL) { + goto nomem; + } + dns_addr_array->MaxCount = num_interfaces; + dns_addr_array->AddrCount = num_interfaces; + if (num_interfaces == 0) { + goto nomem; + } + + dns_addr_array->AddrArray = talloc_zero_array(mem_ctx, struct DNS_ADDR, + num_interfaces); + if (!dns_addr_array->AddrArray) { + TALLOC_FREE(dns_addr_array); + goto nomem; + } + + for (i = 0; i < num_interfaces; i++) { + int ret; + ipstr = iface_list_n_ip(ifaces, i); + if (is_ipaddress_v4(ipstr)) { + have_ipv4 = true; + dns_addr_array->AddrArray[i].MaxSa[0] = 0x02; + ret = inet_pton(AF_INET, ipstr, + &dns_addr_array->AddrArray[i].MaxSa[4]); + } else { + have_ipv6 = true; + dns_addr_array->AddrArray[i].MaxSa[0] = 0x17; + ret = inet_pton(AF_INET6, ipstr, + &dns_addr_array->AddrArray[i].MaxSa[8]); + } + if (ret != 1) { /*yep, 1 means success for inet_pton */ + DBG_ERR("Interface %d address (%s) is invalid\n", + i, ipstr); + goto nomem; + } + } + + if (have_ipv4 && have_ipv6) { + family = 0; /* mixed: MS-DNSP */ + } else if (have_ipv4 && !have_ipv6) { + family = AF_INET; + } else { + family = AF_INET6; + } + dns_addr_array->Family = family; + +nomem: + talloc_free(ifaces); + return dns_addr_array; +} + +struct dnsserver_serverinfo *dnsserver_init_serverinfo(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct ldb_context *samdb) +{ + struct dnsserver_serverinfo *serverinfo; + struct dcerpc_server_info *dinfo; + struct ldb_dn *domain_dn, *forest_dn; + + serverinfo = talloc_zero(mem_ctx, struct dnsserver_serverinfo); + if (serverinfo == NULL) { + return NULL; + } + + dinfo = lpcfg_dcerpc_server_info(mem_ctx, lp_ctx); + if (dinfo) { + serverinfo->dwVersion = (dinfo->version_build & 0x0000FFFF) << 16 | + (dinfo->version_minor & 0x000000FF) << 8 | + (dinfo->version_major & 0x000000FF); + talloc_free(dinfo); + } else { + serverinfo->dwVersion = 0x0ECE0205; /* build, os_minor, os_major */; + } + + serverinfo->fBootMethod = DNS_BOOT_METHOD_DIRECTORY; + serverinfo->fAdminConfigured = 0; + serverinfo->fAllowUpdate = 1; + serverinfo->fDsAvailable = 1; + + serverinfo->pszServerName = talloc_asprintf(mem_ctx, "%s.%s", + lpcfg_netbios_name(lp_ctx), + lpcfg_dnsdomain(lp_ctx)); + + domain_dn = ldb_get_default_basedn(samdb); + forest_dn = ldb_get_root_basedn(samdb); + + serverinfo->pszDsContainer = talloc_asprintf(mem_ctx, + "CN=MicrosoftDNS,DC=DomainDnsZones,%s", + ldb_dn_get_linearized(domain_dn)); + + serverinfo->dwDsForestVersion = dsdb_forest_functional_level(samdb); + serverinfo->dwDsDomainVersion = dsdb_functional_level(samdb); + serverinfo->dwDsDsaVersion = dsdb_dc_functional_level(samdb); + + serverinfo->pszDomainName = samdb_dn_to_dns_domain(mem_ctx, domain_dn); + serverinfo->pszForestName = samdb_dn_to_dns_domain(mem_ctx, forest_dn); + + serverinfo->pszDomainDirectoryPartition = talloc_asprintf(mem_ctx, + "DC=DomainDnsZones,%s", + ldb_dn_get_linearized(domain_dn)); + serverinfo->pszForestDirectoryPartition = talloc_asprintf(mem_ctx, + "DC=ForestDnsZones,%s", + ldb_dn_get_linearized(forest_dn)); + /* IP addresses on which the DNS server listens for DNS requests */ + serverinfo->aipListenAddrs = fill_dns_addr_array(mem_ctx, lp_ctx, true); + + /* All IP addresses available on the server + * Not implemented! + * Use same as listen addresses + */ + serverinfo->aipServerAddrs = serverinfo->aipListenAddrs; + + serverinfo->aipForwarders = NULL; + + serverinfo->aipLogFilter = NULL; + serverinfo->pwszLogFilePath = NULL; + + serverinfo->dwLogLevel = 0; + serverinfo->dwDebugLevel = 0; + serverinfo->dwEventLogLevel = DNS_EVENT_LOG_INFORMATION_TYPE; + serverinfo->dwLogFileMaxSize = 0; + + serverinfo->dwForwardTimeout = 3; /* seconds (default) */ + serverinfo->dwRpcProtocol = 5; + serverinfo->dwNameCheckFlag = DNS_ALLOW_MULTIBYTE_NAMES; + serverinfo->cAddressAnswerLimit = 0; + serverinfo->dwRecursionRetry = 3; /* seconds (default) */ + serverinfo->dwRecursionTimeout = 8; /* seconds (default) */ + serverinfo->dwMaxCacheTtl = 0x00015180; /* 1 day (default) */ + serverinfo->dwDsPollingInterval = 0xB4; /* 3 minutes (default) */ + serverinfo->dwLocalNetPriorityNetMask = 0x000000FF; + + serverinfo->dwScavengingInterval = lpcfg_parm_int( + lp_ctx, NULL, "dnsserver", "ScavengingInterval", 24 * 7); + serverinfo->dwDefaultRefreshInterval = lpcfg_parm_int( + lp_ctx, NULL, "dnsserver", "DefaultRefreshInterval", 24 * 3); + serverinfo->dwDefaultNoRefreshInterval = lpcfg_parm_int( + lp_ctx, NULL, "dnsserver", "DefaultNoRefreshInterval", 24 * 3); + + serverinfo->dwLastScavengeTime = 0; + + serverinfo->fAutoReverseZones = 0; + serverinfo->fAutoCacheUpdate = 0; + + serverinfo->fRecurseAfterForwarding = 0; + serverinfo->fForwardDelegations = 1; + serverinfo->fNoRecursion = 0; + serverinfo->fSecureResponses = 0; + + serverinfo->fRoundRobin = 1; + serverinfo->fLocalNetPriority = 0; + + serverinfo->fBindSecondaries = 0; + serverinfo->fWriteAuthorityNs = 0; + + serverinfo->fStrictFileParsing = 0; + serverinfo->fLooseWildcarding = 0 ; + serverinfo->fDefaultAgingState = 0; + + return serverinfo; +} + +struct dnsserver_zoneinfo *dnsserver_init_zoneinfo(struct dnsserver_zone *zone, + struct dnsserver_serverinfo *serverinfo) +{ + struct dnsserver_zoneinfo *zoneinfo; + uint32_t fReverse; + const char *revzone = "in-addr.arpa"; + const char *revzone6 = "ip6.arpa"; + int len1, len2; + unsigned int i = 0; + + zoneinfo = talloc_zero(zone, struct dnsserver_zoneinfo); + if (zoneinfo == NULL) { + return NULL; + } + + /* If the zone name ends with in-addr.arpa, it's reverse zone */ + /* If the zone name ends with ip6.arpa, it's reverse zone (IPv6) */ + fReverse = 0; + len1 = strlen(zone->name); + len2 = strlen(revzone); + if (len1 > len2 && strcasecmp(&zone->name[len1-len2], revzone) == 0) { + fReverse = 1; + } else { + len2 = strlen(revzone6); + if (len1 > len2 && strcasecmp(&zone->name[len1-len2], revzone6) == 0) { + fReverse = 1; + } + } + + zoneinfo->Version = 0x32; + zoneinfo->Flags = DNS_RPC_ZONE_DSINTEGRATED; + + if (strcmp(zone->name, ".") == 0) { + zoneinfo->dwZoneType = DNS_ZONE_TYPE_CACHE; + zoneinfo->fAllowUpdate = DNS_ZONE_UPDATE_OFF; + zoneinfo->fSecureSecondaries = DNS_ZONE_SECSECURE_NO_SECURITY; + zoneinfo->fNotifyLevel = DNS_ZONE_NOTIFY_OFF; + zoneinfo->dwNoRefreshInterval = 0; + zoneinfo->dwRefreshInterval = 0; + } else { + zoneinfo->Flags |= DNS_RPC_ZONE_UPDATE_SECURE; + zoneinfo->dwZoneType = DNS_ZONE_TYPE_PRIMARY; + zoneinfo->fAllowUpdate = DNS_ZONE_UPDATE_SECURE; + zoneinfo->fSecureSecondaries = DNS_ZONE_SECSECURE_NO_XFER; + zoneinfo->fNotifyLevel = DNS_ZONE_NOTIFY_LIST_ONLY; + zoneinfo->dwNoRefreshInterval = serverinfo->dwDefaultNoRefreshInterval; + zoneinfo->dwRefreshInterval = serverinfo->dwDefaultRefreshInterval; + } + + zoneinfo->fReverse = fReverse; + zoneinfo->fPaused = 0; + zoneinfo->fShutdown = 0; + zoneinfo->fAutoCreated = 0; + zoneinfo->fUseDatabase = 1; + zoneinfo->pszDataFile = NULL; + zoneinfo->aipMasters = NULL; + zoneinfo->aipSecondaries = NULL; + zoneinfo->aipNotify = NULL; + zoneinfo->fUseWins = 0; + zoneinfo->fUseNbstat = 0; + zoneinfo->fAging = 0; + zoneinfo->dwAvailForScavengeTime = 0; + zoneinfo->aipScavengeServers = NULL; + zoneinfo->dwForwarderTimeout = 0; + zoneinfo->fForwarderSlave = 0; + zoneinfo->aipLocalMasters = NULL; + zoneinfo->pwszZoneDn = discard_const_p(char, ldb_dn_get_linearized(zone->zone_dn)); + zoneinfo->dwLastSuccessfulSoaCheck = 0; + zoneinfo->dwLastSuccessfulXfr = 0; + zoneinfo->fQueuedForBackgroundLoad = 0; + zoneinfo->fBackgroundLoadInProgress = 0; + zoneinfo->fReadOnlyZone = 0; + zoneinfo->dwLastXfrAttempt = 0; + zoneinfo->dwLastXfrResult = 0; + + for(i=0; i<zone->num_props; i++){ + bool valid_property; + valid_property = dns_zoneinfo_load_zone_property( + zoneinfo, &zone->tmp_props[i]); + if (!valid_property) { + TALLOC_FREE(zoneinfo); + return NULL; + } + } + + return zoneinfo; +} + +struct dnsserver_zone *dnsserver_find_zone(struct dnsserver_zone *zones, const char *zone_name) +{ + struct dnsserver_zone *z = NULL; + + for (z = zones; z; z = z->next) { + if (samba_dns_name_equal(zone_name, z->name)) { + break; + } + } + + return z; +} + +struct ldb_dn *dnsserver_name_to_dn(TALLOC_CTX *mem_ctx, struct dnsserver_zone *z, const char *name) +{ + struct ldb_dn *dn; + bool ret; + struct ldb_val name_val = + data_blob_string_const(name); + + dn = ldb_dn_copy(mem_ctx, z->zone_dn); + if (dn == NULL) { + return NULL; + } + if (strcasecmp(name, z->name) == 0) { + ret = ldb_dn_add_child_fmt(dn, "DC=@"); + if (!ret) { + talloc_free(dn); + return NULL; + } + return dn; + } + + ret = ldb_dn_add_child_val(dn, + "DC", + name_val); + + if (!ret) { + talloc_free(dn); + return NULL; + } + + return dn; +} + +uint32_t dnsserver_zone_to_request_filter(const char *zone_name) +{ + uint32_t request_filter = 0; + + if (strcmp(zone_name, "..AllZones") == 0) { + request_filter = DNS_ZONE_REQUEST_PRIMARY + | DNS_ZONE_REQUEST_SECONDARY + | DNS_ZONE_REQUEST_AUTO + | DNS_ZONE_REQUEST_FORWARD + | DNS_ZONE_REQUEST_REVERSE + | DNS_ZONE_REQUEST_FORWARDER + | DNS_ZONE_REQUEST_STUB + | DNS_ZONE_REQUEST_DS + | DNS_ZONE_REQUEST_NON_DS + | DNS_ZONE_REQUEST_DOMAIN_DP + | DNS_ZONE_REQUEST_FOREST_DP + | DNS_ZONE_REQUEST_CUSTOM_DP + | DNS_ZONE_REQUEST_LEGACY_DP; + } else if (strcmp(zone_name, "..AllZonesAndCache") == 0) { + request_filter = DNS_ZONE_REQUEST_PRIMARY + | DNS_ZONE_REQUEST_SECONDARY + | DNS_ZONE_REQUEST_CACHE + | DNS_ZONE_REQUEST_AUTO + | DNS_ZONE_REQUEST_FORWARD + | DNS_ZONE_REQUEST_REVERSE + | DNS_ZONE_REQUEST_FORWARDER + | DNS_ZONE_REQUEST_STUB + | DNS_ZONE_REQUEST_DS + | DNS_ZONE_REQUEST_NON_DS + | DNS_ZONE_REQUEST_DOMAIN_DP + | DNS_ZONE_REQUEST_FOREST_DP + | DNS_ZONE_REQUEST_CUSTOM_DP + | DNS_ZONE_REQUEST_LEGACY_DP; + } else if (strcmp(zone_name, "..AllPrimaryZones") == 0) { + request_filter = DNS_ZONE_REQUEST_PRIMARY; + } else if (strcmp(zone_name, "..AllSecondaryZones") == 0) { + request_filter = DNS_ZONE_REQUEST_SECONDARY; + } else if (strcmp(zone_name, "..AllForwardZones") == 0) { + request_filter = DNS_ZONE_REQUEST_FORWARD; + } else if (strcmp(zone_name, "..AllReverseZones") == 0) { + request_filter = DNS_ZONE_REQUEST_REVERSE; + } else if (strcmp(zone_name, "..AllDsZones") == 0) { + request_filter = DNS_ZONE_REQUEST_DS; + } else if (strcmp(zone_name, "..AllNonDsZones") == 0) { + request_filter = DNS_ZONE_REQUEST_NON_DS; + } else if (strcmp(zone_name, "..AllPrimaryReverseZones") == 0) { + request_filter = DNS_ZONE_REQUEST_PRIMARY + | DNS_ZONE_REQUEST_REVERSE; + } else if (strcmp(zone_name, "..AllPrimaryForwardZones") == 0) { + request_filter = DNS_ZONE_REQUEST_PRIMARY + | DNS_ZONE_REQUEST_FORWARD; + } else if (strcmp(zone_name, "..AllSecondaryReverseZones") == 0) { + request_filter = DNS_ZONE_REQUEST_SECONDARY + | DNS_ZONE_REQUEST_REVERSE; + } else if (strcmp(zone_name, "..AllSecondaryForwardZones") == 0) { + request_filter = DNS_ZONE_REQUEST_SECONDARY + | DNS_ZONE_REQUEST_REVERSE; + } + + return request_filter; +} |