/*++ Copyright (c) 1998 Microsoft Corporation Module Name: dconvert.c Abstract: Domain Name System (DNS) Server -- Admin Client Library RPC record conversion routines. Convert DNS_RECORD records into RPC buffer. Author: Jing Chen (t-jingc) June, 1998 reverse functions of rconvert.c Revision History: --*/ #include "dnsclip.h" // // size of string in RPC format // #define STRING_UTF8_BUF_SIZE( string, fUnicode ) \ Dns_GetBufferLengthForStringCopy( \ (string), \ 0, \ ((fUnicode) ? DnsCharSetUnicode : DnsCharSetUtf8), \ DnsCharSetUtf8 ) // // Writing strings to RPC buffer format // #define WRITE_STRING_TO_RPC_BUF(buf, psz, len, funicode) \ Dns_StringCopy( \ (buf), \ NULL, \ (psz), \ (len), \ ((funicode) ? DnsCharSetUnicode : DnsCharSetUtf8), \ DnsCharSetUtf8 ) // // size of name in RPC format // #define NAME_UTF8_BUF_SIZE( string, fUnicode ) \ Dns_GetBufferLengthForStringCopy( \ (string), \ 0, \ ((fUnicode) ? DnsCharSetUnicode : DnsCharSetUtf8), \ DnsCharSetUtf8 ) // // Writing names to RPC buffer format // #define WRITE_NAME_TO_RPC_BUF(buf, psz, len, funicode) \ Dns_StringCopy( \ (buf), \ NULL, \ (psz), \ (len), \ ((funicode) ? DnsCharSetUnicode : DnsCharSetUtf8), \ DnsCharSetUtf8 ) // // Private protos // PDNS_RPC_RECORD Rpc_AllocateRecord( IN DWORD BufferLength ); // // RPC buffer conversion functions // PDNS_RPC_RECORD ADnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Convert A record from DNS Record to RPC buffer. Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; DNS_ASSERT( pRR->wDataLength == sizeof(IP_ADDRESS) ); prpcRR = Rpc_AllocateRecord( sizeof(IP_ADDRESS) ); if ( !prpcRR ) { return NULL; } prpcRR->Data.A.ipAddress = pRR->Data.A.IpAddress; return prpcRR; } PDNS_RPC_RECORD PtrDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Process PTR compatible record from wire. Includes: NS, PTR, CNAME, MB, MR, MG, MD, MF Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; DWORD length; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // PTR data is another domain name // // determine required buffer length and allocate // length = NAME_UTF8_BUF_SIZE(pRR->Data.PTR.pNameHost, funicode); prpcRR = Rpc_AllocateRecord( sizeof(DNS_RPC_NAME) + length ); if ( !prpcRR ) { return NULL; } // // write hostname into buffer, immediately following PTR data struct // prpcRR->Data.PTR.nameNode.cchNameLength = (UCHAR)length; WRITE_NAME_TO_RPC_BUF( prpcRR->Data.PTR.nameNode.achName, // buffer pRR->Data.PTR.pNameHost, 0, funicode ); return prpcRR; } PDNS_RPC_RECORD SoaDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Convert SOA record from DNS Record to RPC buffer. Arguments: pRR - ptr to record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; DWORD length1; DWORD length2; PDNS_RPC_NAME pnamePrimary; PDNS_RPC_NAME pnameAdmin; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // determine required buffer length and allocate // length1 = NAME_UTF8_BUF_SIZE( pRR->Data.SOA.pNamePrimaryServer, funicode ); length2 = NAME_UTF8_BUF_SIZE( pRR->Data.SOA.pNameAdministrator, funicode ); prpcRR = Rpc_AllocateRecord( SIZEOF_SOA_FIXED_DATA + sizeof(DNS_RPC_NAME) * 2 + length1 + length2 ); if ( !prpcRR ) { return NULL; } // // copy fixed fields // RtlCopyMemory( (PCHAR) & prpcRR->Data.SOA.dwSerialNo, (PCHAR) & pRR->Data.SOA.dwSerialNo, SIZEOF_SOA_FIXED_DATA ); // // copy names into RR buffer // - primary server immediately follows SOA data struct // - responsible party follows primary server // pnamePrimary = &prpcRR->Data.SOA.namePrimaryServer; pnamePrimary->cchNameLength = (UCHAR) length1; pnameAdmin = DNS_GET_NEXT_NAME( pnamePrimary ); pnameAdmin->cchNameLength = (UCHAR) length2; WRITE_NAME_TO_RPC_BUF( pnamePrimary->achName, pRR->Data.Soa.pNamePrimaryServer, 0, funicode ); WRITE_NAME_TO_RPC_BUF( pnameAdmin->achName, pRR->Data.Soa.pNameAdministrator, 0, funicode ); return prpcRR; } PDNS_RPC_RECORD TxtDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Read TXT compatible record from wire. Includes: TXT, X25, HINFO, ISDN Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; DWORD bufLength; DWORD length; INT count; PCHAR pch; PCHAR * ppstring; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // determine required buffer length and allocate // - allocate space for each string // - and ptr for each string // bufLength = 0; count = pRR->Data.TXT.dwStringCount; ppstring = pRR->Data.TXT.pStringArray; while ( count-- ) { length = STRING_UTF8_BUF_SIZE( *ppstring++, funicode ); bufLength += sizeof(DNS_RPC_NAME) + length; } // allocate prpcRR = Rpc_AllocateRecord( bufLength ); if ( !prpcRR ) { return NULL; } // // go back through list copying strings to buffer // - ptrs to strings are saved to argv like data section // ppstring walks through this section // - first string written immediately following data section // - each subsequent string immediately follows predecessor // pchbuffer keeps ptr to position to write strings // // JBUGBUG: a mess // pch = (PCHAR) &prpcRR->Data.TXT; ppstring = pRR->Data.TXT.pStringArray; count = pRR->Data.TXT.dwStringCount; while ( count-- ) { length = STRING_UTF8_BUF_SIZE( *ppstring, funicode ); (UCHAR) *pch++ += (UCHAR) length; //+1 for TXT type only length = WRITE_STRING_TO_RPC_BUF( pch, *ppstring++, 0, funicode ); pch += length; #if DBG DNS_PRINT(( "Read text string %s\n", * (ppstring - 1) )); #endif } return prpcRR; } PDNS_RPC_RECORD MinfoDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Read MINFO record from wire. Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; DWORD length1; DWORD length2; PDNS_RPC_NAME prpcName1; PDNS_RPC_NAME prpcName2; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // determine required buffer length and allocate // length1 = NAME_UTF8_BUF_SIZE( pRR->Data.MINFO.pNameMailbox, funicode ); length2 = NAME_UTF8_BUF_SIZE( pRR->Data.MINFO.pNameErrorsMailbox, funicode ); prpcRR = Rpc_AllocateRecord( sizeof(DNS_RPC_NAME) * 2 + length1 + length2 ); if ( !prpcRR ) { return NULL; } // // copy names into RR buffer // - mailbox immediately follows MINFO data struct // - errors mailbox immediately follows primary server // prpcName1 = &prpcRR->Data.MINFO.nameMailBox; prpcName1->cchNameLength = (UCHAR) length1; prpcName2 = DNS_GET_NEXT_NAME( prpcName1); prpcName2->cchNameLength = (UCHAR) length2; WRITE_NAME_TO_RPC_BUF( prpcName1->achName, pRR->Data.MINFO.pNameMailbox, 0, funicode ); WRITE_NAME_TO_RPC_BUF( prpcName2->achName, pRR->Data.MINFO.pNameErrorsMailbox, 0, funicode ); return prpcRR; } PDNS_RPC_RECORD MxDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: convert MX compatible record. Includes: MX, RT, AFSDB Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; PDNS_RPC_NAME prpcName; DWORD length; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // determine required buffer length and allocate // length = NAME_UTF8_BUF_SIZE( pRR->Data.MX.pNameExchange, funicode ); prpcRR = Rpc_AllocateRecord( SIZEOF_MX_FIXED_DATA + sizeof(DNS_RPC_NAME) + length ); if ( !prpcRR ) { return NULL; } // // copy preference // prpcRR->Data.MX.wPreference = pRR->Data.MX.wPreference; // // write hostname into buffer, immediately following MX struct // prpcName = &prpcRR->Data.MX.nameExchange; prpcName->cchNameLength = (UCHAR) length; WRITE_NAME_TO_RPC_BUF( prpcName->achName, pRR->Data.MX.pNameExchange, 0, funicode ); return prpcRR; } PDNS_RPC_RECORD FlatDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Convert memory copy compatible record. Includes AAAA and WINS types. Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; DWORD bufLength; // // determine required buffer length and allocate // bufLength = pRR->wDataLength; prpcRR = Rpc_AllocateRecord( bufLength ); if ( !prpcRR ) { return NULL; } // // copy packet data to record // RtlCopyMemory( & prpcRR->Data, & pRR->Data, bufLength ); return prpcRR; } PDNS_RPC_RECORD SrvDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: convert SRV record. Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; PDNS_RPC_NAME prpcName; DWORD length; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // determine required buffer length and allocate // length = NAME_UTF8_BUF_SIZE( pRR->Data.SRV.pNameTarget, funicode ); prpcRR = Rpc_AllocateRecord( SIZEOF_SRV_FIXED_DATA + sizeof(DNS_RPC_NAME) + length ); if ( !prpcRR ) { return NULL; } // // copy SRV fixed fields // prpcRR->Data.SRV.wPriority = pRR->Data.SRV.wPriority; prpcRR->Data.SRV.wWeight = pRR->Data.SRV.wWeight; prpcRR->Data.SRV.wPort = pRR->Data.SRV.wPort; // // write hostname into buffer, immediately following SRV struct // prpcName = &prpcRR->Data.SRV.nameTarget; prpcName->cchNameLength = (UCHAR) length; WRITE_NAME_TO_RPC_BUF( prpcName->achName, pRR->Data.SRV.pNameTarget, 0, funicode ); return prpcRR; } PDNS_RPC_RECORD NbstatDnsRecordConvert( IN PDNS_RECORD pRR ) /*++ Routine Description: Read WINSR record from wire. Arguments: pRR - record being read Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRR; PDNS_RPC_NAME prpcName; DWORD length; BOOL funicode = IS_UNICODE_RECORD( pRR ); // // determine required buffer length and allocate // length = NAME_UTF8_BUF_SIZE( pRR->Data.WINSR.pNameResultDomain, funicode ); prpcRR = Rpc_AllocateRecord( SIZEOF_NBSTAT_FIXED_DATA + sizeof(DNS_RPC_NAME) + length ); if ( !prpcRR ) { return NULL; } // // copy WINSR fixed fields // prpcRR->Data.WINSR.dwMappingFlag = pRR->Data.WINSR.dwMappingFlag; prpcRR->Data.WINSR.dwLookupTimeout = pRR->Data.WINSR.dwLookupTimeout; prpcRR->Data.WINSR.dwCacheTimeout = pRR->Data.WINSR.dwCacheTimeout; // // write hostname into buffer, immediately following WINSR struct // prpcName = &prpcRR->Data.WINSR.nameResultDomain; prpcName->cchNameLength = (UCHAR) length; WRITE_NAME_TO_RPC_BUF( prpcName->achName, pRR->Data.WINSR.pNameResultDomain, 0, funicode ); return prpcRR; } // // Jump table for DNS_RECORD => RPC buffer conversion. // typedef PDNS_RPC_RECORD (* RECORD_TO_RPC_CONVERT_FUNCTION)( PDNS_RECORD ); RECORD_TO_RPC_CONVERT_FUNCTION RecordToRpcConvertTable[] = { NULL, // ZERO ADnsRecordConvert, // A PtrDnsRecordConvert, // NS PtrDnsRecordConvert, // MD PtrDnsRecordConvert, // MF PtrDnsRecordConvert, // CNAME SoaDnsRecordConvert, // SOA PtrDnsRecordConvert, // MB PtrDnsRecordConvert, // MG PtrDnsRecordConvert, // MR NULL, // NULL FlatDnsRecordConvert, // WKS PtrDnsRecordConvert, // PTR TxtDnsRecordConvert, // HINFO MinfoDnsRecordConvert, // MINFO MxDnsRecordConvert, // MX TxtDnsRecordConvert, // TXT MinfoDnsRecordConvert, // RP MxDnsRecordConvert, // AFSDB TxtDnsRecordConvert, // X25 TxtDnsRecordConvert, // ISDN MxDnsRecordConvert, // RT NULL, // NSAP NULL, // NSAPPTR NULL, // SIG NULL, // KEY NULL, // PX NULL, // GPOS FlatDnsRecordConvert, // AAAA NULL, // 29 NULL, // 30 NULL, // 31 NULL, // 32 SrvDnsRecordConvert, // SRV // // NOTE: last type indexed by type ID MUST be set // as MAX_SELF_INDEXED_TYPE #define in record.h // (see note above in record info table) // note these follow, but require OFFSET_TO_WINS_RR subtraction // from actual type value NULL, // DNS_TYPE_ATMA NULL, // 0x0023 NULL, // 0x0024 NULL, // 0x0025 NULL, // 0x0026 NULL, // 0x0027 NULL, // 0x0028 NULL, // DNS_TYPE_TKEY NULL, // DNS_TYPE_TSIG FlatDnsRecordConvert, // WINS NbstatDnsRecordConvert // WINS-R }; PDNS_RPC_RECORD Rpc_AllocateRecord( IN DWORD BufferLength ) /*++ Routine Description: Allocate RPC record structure. Arguments: wBufferLength - desired buffer length (beyond structure header) Return Value: Ptr to buffer. NULL on error. --*/ { PDNS_RPC_RECORD prr; if ( BufferLength > MAXWORD ) { return NULL; } prr = ALLOCATE_HEAP( SIZEOF_DNS_RPC_RECORD_HEADER + BufferLength ); if ( !prr ) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return NULL; } // set datalength to buffer length prr->wDataLength = (WORD) BufferLength; return( prr ); } PDNS_RPC_RECORD DnsConvertRecordToRpcBuffer( IN PDNS_RECORD pRecord ) /*++ Routine Description: Convert standard DNS record to RPC buffer. Arguments: pRecord -- DNS Record to be converted. //fUnicode -- flag, write records into unicode Return Value: Ptr to new RPC buffer if successful. NULL on failure. --*/ { PDNS_RPC_RECORD prpcRecord; WORD index; WORD type; RECORD_TO_RPC_CONVERT_FUNCTION pFunc; DNS_ASSERT( DNS_IS_DWORD_ALIGNED(pRecord) ); IF_DNSDBG( RPC2 ) { DNS_PRINT(( "Enter DnsConvertRecordToRpcBuffer()\n" " pRecord = %p\n", pRecord )); } // // convert record // set unicode flag if converting // type = pRecord->wType; index = INDEX_FOR_TYPE( type ); DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX ); if ( !index || !(pFunc = RecordToRpcConvertTable[ index ]) ) { // if unknown type try flat record copy -- best we can // do to protect if server added new types since admin built DNS_PRINT(( "ERROR: no DNS_RECORD to RPC conversion routine for type %d.\n" " using flat conversion routine.\n", type )); pFunc = FlatDnsRecordConvert; } prpcRecord = (*pFunc)( pRecord ); if ( ! prpcRecord ) { DNS_PRINT(( "ERROR: Record build routine failure for record type %d.\n" " status = %p\n\n", type, GetLastError() )); return NULL; } // // fill out record structure // prpcRecord->wType = type; prpcRecord->dwTtlSeconds = pRecord->dwTtl; IF_DNSDBG( INIT ) { DNS_PRINT(( "New RPC buffer built\n" )); } IF_DNSDBG( RPC2 ) { DNS_PRINT(( "Finished DnsConvertRpcBufferToRecords()" )); } return prpcRecord; } // // End dconvert.c //