/*++ Copyright (c) 1995 Microsoft Corporation Module Name: local.c Abstract: Domain Name System (DNS) Server -- Admin Client API DNS Admin API calls that do not use RPC. Completely executed in client library. Author: Jim Gilroy (jamesg) 14-Oct-1995 Environment: User Mode - Win32 Revision History: --*/ #include "dnsclip.h" // // Debug globals // DWORD LocalDebugFlag; // // Buffer size for building WKS services string // #define WKS_SERVICES_BUFFER_SIZE (0x1000) // 4k VOID DNS_API_FUNCTION DnssrvInitializeDebug( VOID ) /*++ Routine Description: Initialize debugging -- use dnslib debugging. Only purpose is generic interface that hides file flag and name info so no need to put in header. --*/ { #if DBG Dns_StartDebug( 0, DNSRPC_DEBUG_FLAG_FILE, & LocalDebugFlag, DNSRPC_DEBUG_FILE_NAME, 1000000 // 1mb wrap ); DNS_PRINT(( "LocalDebugFlag = %p\n", LocalDebugFlag )); #endif } PVOID DnssrvMidlAllocZero( IN DWORD dwSize ) /*++ Routine Description: MIDL allocate and zero memory. Arguments: Return Value: Ptr to allocated and zeroed memory. --*/ { PVOID ptr; ptr = MIDL_user_allocate( dwSize ); if ( !ptr ) { return( NULL ); } RtlZeroMemory( ptr, dwSize ); return( ptr ); } VOID DNS_API_FUNCTION DnssrvFreeRpcBuffer( IN OUT PDNS_RPC_BUFFER pBuf ) /*++ Routine Description: Free generic (no substructures) RPC buffer. Arguments: pBuf -- ptr to buf to free Return Value: None --*/ { if ( !pBuf ) { return; } MIDL_user_free( pBuf ); } VOID DNS_API_FUNCTION DnssrvFreeServerInfo( IN OUT PDNS_RPC_SERVER_INFO pServerInfo ) /*++ Routine Description: Deep free of DNS_SERVER_INFO structure. Arguments: pServerInfo -- ptr to server info to free Return Value: None --*/ { if ( !pServerInfo ) { return; } // // free allocated items inside the server info blob // if ( pServerInfo->pszServerName ) { MIDL_user_free( pServerInfo->pszServerName ); } if ( pServerInfo->aipServerAddrs ) { MIDL_user_free( pServerInfo->aipServerAddrs ); } if ( pServerInfo->aipListenAddrs ) { MIDL_user_free( pServerInfo->aipListenAddrs ); } if ( pServerInfo->aipForwarders ) { MIDL_user_free( pServerInfo->aipForwarders ); } if ( pServerInfo->aipLogFilter ) { MIDL_user_free( pServerInfo->aipLogFilter ); } if ( pServerInfo->pwszLogFilePath ) { MIDL_user_free( pServerInfo->pwszLogFilePath ); } if ( pServerInfo->pszDsContainer ) { MIDL_user_free( pServerInfo->pszDsContainer ); } if ( pServerInfo->pszDomainName ) { MIDL_user_free( pServerInfo->pszDomainName ); } if ( pServerInfo->pszForestName ) { MIDL_user_free( pServerInfo->pszForestName ); } if ( pServerInfo->pszDomainDirectoryPartition ) { MIDL_user_free( pServerInfo->pszDomainDirectoryPartition ); } if ( pServerInfo->pszForestDirectoryPartition ) { MIDL_user_free( pServerInfo->pszForestDirectoryPartition ); } // // free DNS_SERVER_INFO struct itself // MIDL_user_free( pServerInfo ); } VOID DNS_API_FUNCTION DnssrvFreeZoneInfo( IN OUT PDNS_RPC_ZONE_INFO pZoneInfo ) /*++ Routine Description: Deep free of DNS_ZONE_INFO structure. Arguments: pZoneInfo -- ptr to zone info to free Return Value: None --*/ { if ( !pZoneInfo ) { return; } // // free substructures // - name string // - data file string // - secondary IP array // - WINS server array // if ( pZoneInfo->pszZoneName ) { MIDL_user_free( pZoneInfo->pszZoneName ); } if ( pZoneInfo->pszDataFile ) { MIDL_user_free( pZoneInfo->pszDataFile ); } if ( pZoneInfo->aipMasters ) { MIDL_user_free( pZoneInfo->aipMasters ); } if ( pZoneInfo->aipSecondaries ) { MIDL_user_free( pZoneInfo->aipSecondaries ); } if ( pZoneInfo->pszDpFqdn ) { MIDL_user_free( pZoneInfo->pszDpFqdn ); } if ( pZoneInfo->pwszZoneDn ) { MIDL_user_free( pZoneInfo->pwszZoneDn ); } // // free DNS_ZONE_INFO struct itself // MIDL_user_free( pZoneInfo ); } VOID DNS_API_FUNCTION DnssrvFreeNode( IN OUT PDNS_NODE pNode, IN BOOLEAN fFreeRecords ) { if ( pNode->pRecord ) { Dns_RecordListFree( pNode->pRecord, TRUE ); } if ( pNode->Flags.S.FreeOwner ) { FREE_HEAP( pNode->pName ); } FREE_HEAP( pNode ); } VOID DNS_API_FUNCTION DnssrvFreeNodeList( IN OUT PDNS_NODE pNode, IN BOOLEAN fFreeRecords ) { PDNS_NODE pnext; // free all nodes in list while ( pNode ) { pnext = pNode->pNext; DnssrvFreeNode( pNode, fFreeRecords ); pNode = pnext; } } VOID DNS_API_FUNCTION DnssrvFreeZone( IN OUT PDNS_RPC_ZONE pZone ) /*++ Routine Description: Deep free of DNS_RPC_ZONE structure. Arguments: pZone -- ptr to zone to free Return Value: None --*/ { if ( !pZone ) { return; } // free zone name, then zone itself if ( pZone->pszZoneName ) { MIDL_user_free( pZone->pszZoneName ); } if ( pZone->pszDpFqdn ) { MIDL_user_free( pZone->pszDpFqdn ); } MIDL_user_free( pZone ); } VOID DNS_API_FUNCTION DnssrvFreeZoneList( IN OUT PDNS_RPC_ZONE_LIST pZoneList ) /*++ Routine Description: Deep free of list of DNS_RPC_ZONE structures. Arguments: pZoneList -- ptr RPC_ZONE_LIST structure to free Return Value: None --*/ { DWORD i; PDNS_RPC_ZONE pzone; if ( !pZoneList ) { return; } for( i=0; i< pZoneList->dwZoneCount; i++ ) { // zone name is only sub-structure pzone = pZoneList->ZoneArray[i]; MIDL_user_free( pzone->pszZoneName ); MIDL_user_free( pzone ); } MIDL_user_free( pZoneList ); } VOID DNS_API_FUNCTION DnssrvFreeDirectoryPartitionEnum( IN OUT PDNS_RPC_DP_ENUM pDp ) /*++ Routine Description: Deep free of PDNS_RPC_DP_ENUM structure. Arguments: pDp -- ptr to directory partition to free Return Value: None --*/ { if ( !pDp ) { return; } if ( pDp->pszDpFqdn ) { MIDL_user_free( pDp->pszDpFqdn ); } MIDL_user_free( pDp ); } VOID DNS_API_FUNCTION DnssrvFreeDirectoryPartitionInfo( IN OUT PDNS_RPC_DP_INFO pDp ) /*++ Routine Description: Deep free of PDNS_RPC_DP_INFO structure. Arguments: pDp -- ptr to directory partition to free Return Value: None --*/ { DWORD i; if ( !pDp ) { return; } if ( pDp->pszDpFqdn ) { MIDL_user_free( pDp->pszDpFqdn ); } if ( pDp->pszDpDn ) { MIDL_user_free( pDp->pszDpDn ); } if ( pDp->pszCrDn ) { MIDL_user_free( pDp->pszCrDn ); } for( i = 0; i < pDp->dwReplicaCount; i++ ) { PDNS_RPC_DP_REPLICA p = pDp->ReplicaArray[ i ]; if ( p ) { if ( p->pszReplicaDn ) { MIDL_user_free( p->pszReplicaDn ); } MIDL_user_free( p ); } } MIDL_user_free( pDp ); } VOID DNS_API_FUNCTION DnssrvFreeDirectoryPartitionList( IN OUT PDNS_RPC_DP_LIST pDpList ) /*++ Routine Description: Deep free of list of PDNS_RPC_DP_LIST structures. Arguments: pZoneList -- ptr PDNS_RPC_DP_LIST structure to free Return Value: None --*/ { DWORD i; PDNS_RPC_DP_ENUM pDp; if ( !pDpList ) { return; } for( i=0; i < pDpList->dwDpCount; ++i ) { pDp = pDpList->DpArray[ i ]; DnssrvFreeDirectoryPartitionEnum( pDp ); } MIDL_user_free( pDpList ); } PCHAR DnssrvGetWksServicesInRecord( IN PDNS_FLAT_RECORD pRR ) /*++ Routine Description: Get list of services in WKS record. Arguments: pRR - flat WKS record being written Return Value: Ptr to services string, caller MUST free. NULL on error. --*/ { struct servent * pServent; struct protoent * pProtoent; INT i; DWORD length; USHORT port; UCHAR bBitmask; CHAR buffer[ WKS_SERVICES_BUFFER_SIZE ]; PCHAR pch = buffer; PCHAR pchstart; PCHAR pchstop; // protocol pProtoent = getprotobynumber( (INT) pRR->Data.WKS.chProtocol ); if ( ! pProtoent ) { DNS_PRINT(( "ERROR: Unable to find protocol %d, writing WKS record.\n", (INT) pRR->Data.WKS.chProtocol )); return( NULL ); } // // services // // find each bit set in bitmask, lookup and write service // corresponding to that port // // note, that since that port zero is the front of port bitmask, // lowest ports are the highest bits in each byte // pchstart = pch; pchstop = pch + WKS_SERVICES_BUFFER_SIZE; for ( i = 0; i < (INT)(pRR->wDataLength - SIZEOF_WKS_FIXED_DATA); i++ ) { bBitmask = (UCHAR) pRR->Data.WKS.bBitMask[i]; port = i * 8; // write service name for each bit set in byte // - get out as soon byte is empty of ports // - terminate each name with blank (until last) while ( bBitmask ) { if ( bBitmask & 0x80 ) { pServent = getservbyport( (INT) htons(port), pProtoent->p_name ); if ( pServent ) { INT copyCount = strlen(pServent->s_name); pch++; if ( pchstop - pch <= copyCount+1 ) { return( NULL ); } RtlCopyMemory( pch, pServent->s_name, copyCount ); pch += copyCount; *pch = ' '; } else { DNS_PRINT(( "ERROR: Unable to find service for port %d, " "writing WKS record.\n", port )); pch += sprintf( pch, "%d", port ); } } port++; // next service port bBitmask <<= 1; // shift mask up to read next port } } // NULL terminate services string // and determine length *pch++ = 0; length = (DWORD) (pch - pchstart); // allocate copy of this string pch = ALLOCATE_HEAP( length ); if ( !pch ) { SetLastError( DNS_ERROR_NO_MEMORY ); return( NULL ); } RtlCopyMemory( pch, pchstart, length ); return( pch ); } // // Build LDAP \ DS names for objects // // // Build Unicode LDAP paths // #define DN_TEXT(string) (L##string) LPWSTR DNS_API_FUNCTION DnssrvCreateDsNodeName( IN PDNS_RPC_SERVER_INFO pServerInfo, IN LPWSTR pszZone, IN LPWSTR pszNode ) /*++ Routine Description: Build node DS name. Arguments: pServerInfo -- server info for server pszZone -- zone name pszNode -- node name RELATIVE to zone root Return Value: Ptr to node's DS name. Caller must free. NULL on error. --*/ { PWCHAR psznodeDN; DWORD length; // if not DS integrated, bail if ( !pServerInfo->pszDsContainer ) { return( NULL ); } // special case zone root if ( !pszNode ) { pszNode = DN_TEXT("@"); } // allocate required space length = sizeof(DN_TEXT("dc=,dc=, ")); length += sizeof(WCHAR) * wcslen( pszNode ); length += sizeof(WCHAR) * wcslen( pszZone ); length += sizeof(WCHAR) * wcslen( (LPWSTR)pServerInfo->pszDsContainer ); psznodeDN = (PWCHAR) ALLOCATE_HEAP( length ); if ( !psznodeDN ) { return( psznodeDN ); } // build DN wcscpy( psznodeDN, DN_TEXT("dc=") ); wcscat( psznodeDN, pszNode ); length = wcslen(psznodeDN); ASSERT ( length > 3 ); if ( length != 4 && // "dc=." case psznodeDN[ length - 1 ] == '.' ) { // // we have a dot terminated node name, strip it out // psznodeDN[ length - 1 ] = '\0'; } wcscat( psznodeDN, DN_TEXT(",dc=") ); wcscat( psznodeDN, pszZone ); length = wcslen(psznodeDN); ASSERT ( length > 1 ); if ( 1 != wcslen ( pszZone ) && // zone = "." case psznodeDN[ length - 1 ] == '.' ) { // // we have a dot terminated zone name, strip it out // psznodeDN[ length - 1 ] = '\0'; } wcscat( psznodeDN, DN_TEXT(",") ); wcscat( psznodeDN, (LPWSTR)pServerInfo->pszDsContainer ); DNSDBG( STUB, ( "Node DN built: %s\n", psznodeDN )); return( psznodeDN ); } LPWSTR DNS_API_FUNCTION DnssrvCreateDsZoneName( IN PDNS_RPC_SERVER_INFO pServerInfo, IN LPWSTR pszZone ) /*++ Routine Description: Build zone DS name. This routine should only be used for legacy zones on W2K servers. For Whistler+ servers the zone info structure has the zone object DN. Arguments: pServerInfo -- server info for server pszZone -- zone name Return Value: Ptr to zone's DS name. Caller must free. NULL on error. --*/ { PWCHAR pszzoneDN; DWORD length; // if not DS integrated, bail if ( !(LPWSTR)pServerInfo->pszDsContainer ) { return( NULL ); } // allocate required space length = sizeof(DN_TEXT("dc=, ")); length += sizeof(WCHAR) * wcslen( pszZone ); length += sizeof(WCHAR) * wcslen( (LPWSTR)pServerInfo->pszDsContainer ); pszzoneDN = (PWCHAR) ALLOCATE_HEAP( length ); if ( !pszzoneDN ) { return( pszzoneDN ); } // build DN wcscpy( pszzoneDN, DN_TEXT("dc=") ); wcscat( pszzoneDN, pszZone ); length = wcslen(pszzoneDN); ASSERT ( length > 1 ); if ( length != 4 && // "dc=." case pszzoneDN[ length - 1 ] == '.' ) { // // we have a dot terminated zone name, strip it out // pszzoneDN[ length - 1 ] = '\0'; } wcscat( pszzoneDN, DN_TEXT(",") ); wcscat( pszzoneDN, (LPWSTR)pServerInfo->pszDsContainer ); DNSDBG( STUB, ( "Zone DN built: %s\n", pszzoneDN )); return( pszzoneDN ); } LPWSTR DNS_API_FUNCTION DnssrvCreateDsServerName( IN PDNS_RPC_SERVER_INFO pServerInfo ) /*++ Routine Description: Build zone DS name. Arguments: pServerInfo -- server info for server Return Value: Ptr to server's DS name. Caller must free. NULL on error. --*/ { PWCHAR pszserverDN; DWORD length; // // DEVNOTE: need investigation here, // may just be able to use DNS folder in DS // // if not DS integrated, bail if ( !(LPWSTR)pServerInfo->pszDsContainer ) { return( NULL ); } // allocate space length = sizeof(DN_TEXT(" ")); length += sizeof(WCHAR) * wcslen( (LPWSTR)pServerInfo->pszDsContainer ); pszserverDN = (PWCHAR) ALLOCATE_HEAP( length ); if ( !pszserverDN ) { return( pszserverDN ); } // build DN wcscpy( pszserverDN, (LPWSTR)pServerInfo->pszDsContainer ); DNSDBG( STUB, ( "Server DN built: %s\n", pszserverDN )); return( pszserverDN ); } // // End local.c // #if 0 VOID convertRpcUnionTypeToUnicode( IN DWORD dwTypeId, IN OUT DNS_RPC_UNION pData ) /*++ Routine Description: Convert RPC union types to unicode. Arguments: Return Value: None --*/ { switch ( dwTypeId ) { case DNSSRV_TYPEID_LPSTR: pwideString = DnsStringCopyAllocateEx( pData.String, 0, FALSE, // UTF8 in TRUE // Unicode out ); if ( !pwideString ) { ASSERT( FALSE ); return; } MIDL_user_free( pData.String ); pData.String = (LPSTR) pwideString; case DNSSRV_TYPEID_SERVER_INFO: DnsPrint_RpcServerInfo( PrintRoutine, pszHeader, (PDNS_RPC_SERVER_INFO) pData ); break; case DNSSRV_TYPEID_ZONE: DnsPrint_RpcZone( PrintRoutine, pszHeader, (PDNS_RPC_ZONE) pData ); break; case DNSSRV_TYPEID_ZONE_INFO: DnsPrint_RpcZoneInfo( PrintRoutine, pszHeader, (PDNS_RPC_ZONE_INFO) pData ); break; case DNSSRV_TYPEID_ZONE_DBASE_INFO: PrintRoutine( "%sZone Dbase Info:\n" "\tDS Integrated = %d\n" "\tFile Name = %s\n", pszHeader, ((PDNS_RPC_ZONE_DBASE_INFO)pData)->fDsIntegrated, ((PDNS_RPC_ZONE_DBASE_INFO)pData)->pszFileName ); break; } VOID convertStringToUnicodeInPlace( IN LPSTR * ppszString ) /*++ Routine Description: Convert string to unicode and return it to its current position in structure. Arguments: Return Value: None --*/ { switch ( dwTypeId ) { case DNSSRV_TYPEID_LPSTR: pwideString = Dns_StringCopyAllocateEx( pData.String, 0, FALSE, // UTF8 in TRUE // Unicode out ); if ( !pwideString ) { ASSERT( FALSE ); return; } MIDL_user_free( pData.String ); pData.String = (LPSTR) pwideString; case DNSSRV_TYPEID_SERVER_INFO: DnsPrint_RpcServerInfo( PrintRoutine, pszHeader, (PDNS_RPC_SERVER_INFO) pData ); break; case DNSSRV_TYPEID_STATS: DnsPrint_RpcStatistics( PrintRoutine, pszHeader, (PDNS_RPC_STATISTICS) pData ); break; case DNSSRV_TYPEID_ZONE: DnsPrint_RpcZone( PrintRoutine, pszHeader, (PDNS_RPC_ZONE) pData ); break; case DNSSRV_TYPEID_FORWARDERS: DnsPrint_RpcIpArrayPlusParameters( PrintRoutine, pszHeader, "Forwarders Info:", "Slave", ((PDNS_RPC_FORWARDERS)pData)->fSlave, "Timeout", ((PDNS_RPC_FORWARDERS)pData)->dwForwardTimeout, "\tForwarders:\n", ((PDNS_RPC_FORWARDERS)pData)->aipForwarders ); break; case DNSSRV_TYPEID_ZONE_INFO: DnsPrint_RpcZoneInfo( PrintRoutine, pszHeader, (PDNS_RPC_ZONE_INFO) pData ); break; case DNSSRV_TYPEID_ZONE_SECONDARIES: DnsPrint_RpcIpArrayPlusParameters( PrintRoutine, pszHeader, "Zone Secondary Info:", "Secure Secondaries", ((PDNS_RPC_ZONE_SECONDARIES)pData)->fSecureSecondaries, NULL, 0, "\tSecondaries:\n", ((PDNS_RPC_ZONE_SECONDARIES)pData)->aipSecondaries ); break; case DNSSRV_TYPEID_ZONE_TYPE_RESET: DnsPrint_RpcIpArrayPlusParameters( PrintRoutine, pszHeader, "Zone Type Reset Info:", "ZoneType", ((PDNS_RPC_ZONE_TYPE_RESET)pData)->dwZoneType, NULL, 0, "\tMasters:\n", ((PDNS_RPC_ZONE_TYPE_RESET)pData)->aipMasters ); break; case DNSSRV_TYPEID_ZONE_DBASE_INFO: PrintRoutine( "%sZone Dbase Info:\n" "\tDS Integrated = %d\n" "\tFile Name = %s\n", pszHeader, ((PDNS_RPC_ZONE_DBASE_INFO)pData)->fDsIntegrated, ((PDNS_RPC_ZONE_DBASE_INFO)pData)->pszFileName ); break; default: PrintRoutine( "%s\n" "WARNING: Unknown RPC structure typeid = %d at %p\n", dwTypeId, pData ); break; } } #endif PDNSSRV_STAT DNS_API_FUNCTION DnssrvFindStatisticsInBuffer( IN PDNS_RPC_BUFFER pBuffer, IN DWORD StatId ) /*++ Routine Description: Finds desired statistics in stats buffer. Arguments: pStatsBuf -- stats buffer StatId -- ID of desired stats Return Value: Ptr to desired stats in buffer. --*/ { PDNSSRV_STAT pstat; PCHAR pch; PCHAR pchstop; pch = pBuffer->Buffer; pchstop = pch + pBuffer->dwLength; // // check all stat blobs within buffer // while ( pch < pchstop ) { pstat = (PDNSSRV_STAT) pch; pch = (PCHAR) GET_NEXT_STAT_IN_BUFFER( pstat ); if ( pch > pchstop ) { DNS_PRINT(( "ERROR: invalid stats buffer\n" )); break; } // found matching stats // - verify correct length // - return if ( pstat->Header.StatId == StatId ) { if ( DnssrvValidityCheckStatistic(pstat) != ERROR_SUCCESS ) { DNS_PRINT(( "WARNING: Mismatched stats length.\n" )); break; } return( pstat ); } } return( NULL ); }