/*++ Copyright (c) 1997-2001 Microsoft Corporation Module Name: print.c Abstract: Domain Name System (DNS) Library Print routines. Author: Jim Gilroy (jamesg) February 8, 1997 Revision History: --*/ #include "local.h" #include "svcguid.h" // RnR guids #include "..\dnsapi\dnsapip.h" // Private query stuff #include "..\resolver\idl\resrpc.h" // Resolver interface structs // // Print globals // CRITICAL_SECTION DnsAtomicPrintCs; PCRITICAL_SECTION pDnsAtomicPrintCs = NULL; // // Empty string for simple switching of UTF-8/Unicode print // (macros in dnslib.h) // DWORD DnsEmptyString = 0; // // Indenting // // Serve up as many indenting tabs as indent level indicates // CHAR IndentString[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; #define INDENT_STRING( level ) (IndentString + (sizeof(IndentString) - 1 - (level))) // // Print Locking // // Unless caller initilizes print locking by supplying lock, // print locking is disabled. // VOID DnsPrint_InitLocking( IN PCRITICAL_SECTION pLock ) /*++ Routine Description: Setup DNS printing to use a lock. Can use already initialized lock from caller, or will create default lock. Arguments: pLock - ptr to CS to use as lock; if NULL, create one Return Value: None --*/ { if ( pLock ) { pDnsAtomicPrintCs = pLock; } else if ( !pDnsAtomicPrintCs ) { InitializeCriticalSection( &DnsAtomicPrintCs ); pDnsAtomicPrintCs = &DnsAtomicPrintCs; } } VOID DnsPrint_Lock( VOID ) /*++ Routine Description: Lock to get atomic DNS print. --*/ { if ( pDnsAtomicPrintCs ) { EnterCriticalSection( pDnsAtomicPrintCs ); } } VOID DnsPrint_Unlock( VOID ) /*++ Routine Description: Unlock to debug print. --*/ { if ( pDnsAtomicPrintCs ) { LeaveCriticalSection( pDnsAtomicPrintCs ); } } // // Print routines for general types and structures // VOID DnsPrint_String( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader OPTIONAL, IN PSTR pszString, IN BOOL fUnicode, IN PSTR pszTrailer OPTIONAL ) /*++ Routine Description: Print DNS string given in either Unicode or UTF-8 format. --*/ { if ( !pszHeader ) { pszHeader = ""; } if ( !pszTrailer ) { pszTrailer = ""; } if ( ! pszString ) { PrintRoutine( pContext, "%s(NULL DNS string ptr)%s", pszHeader, pszTrailer ); } else if (fUnicode) { PrintRoutine( pContext, "%s%S%s", pszHeader, (PWSTR ) pszString, pszTrailer ); } else { PrintRoutine( pContext, "%s%s%s", pszHeader, (PSTR ) pszString, pszTrailer ); } } VOID DnsPrint_StringCharSet( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader OPTIONAL, IN PSTR pszString, IN DNS_CHARSET CharSet, IN PSTR pszTrailer OPTIONAL ) /*++ Routine Description: Print string of given CHARSET. --*/ { DnsPrint_String( PrintRoutine, pContext, pszHeader, pszString, (CharSet == DnsCharSetUnicode), pszTrailer ); } VOID DnsPrint_UnicodeStringBytes( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PWCHAR pUnicode, IN DWORD Length ) /*++ Routine Description: Print chars (WORDs) of unicode string. --*/ { DWORD i; PrintRoutine( pContext, "%s\r\n" "\twide string %S\r\n" "\tlength %d\r\n" "\tbytes ", pszHeader, pUnicode, Length ); for ( i=0; i 0x%p (%d)\r\n", pszName, i, adwArray[i], adwArray[i] ); } DnsPrint_Unlock(); } VOID DnsPrint_IpAddressArray( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PSTR pszName, IN DWORD dwIpAddrCount, IN PIP_ADDRESS pIpAddrs ) /*++ Routine Description: Print IP address array. --*/ { DWORD i; DnsPrint_Lock(); if ( !pszName ) { pszName = "IP Addr"; } PrintRoutine( pContext, "%s%s Count = %d\r\n", pszHeader ? pszHeader : "", pszName, dwIpAddrCount ); if ( dwIpAddrCount != 0 && pIpAddrs != NULL ) { // print array with count // use character print so works even if NOT DWORD aligned for( i=0; i %d.%d.%d.%d\r\n", pszName, i, * ( (PUCHAR) &pIpAddrs[i] + 0 ), * ( (PUCHAR) &pIpAddrs[i] + 1 ), * ( (PUCHAR) &pIpAddrs[i] + 2 ), * ( (PUCHAR) &pIpAddrs[i] + 3 ) ); } } #if 0 // this spins if printing zero length IP_ARRAY struct else if ( pIpAddrs != NULL ) { // print NULL terminated array (ex. hostents IPs) i = 0; while ( pIpAddrs[i] ) { PrintRoutine( pContext, "\t%s[%d] => %s\r\n", pszName, i, inet_ntoa( *(struct in_addr *) &pIpAddrs[i] ) ); } } #endif DnsPrint_Unlock(); } VOID DnsPrint_IpArray( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PSTR pszName, IN PIP_ARRAY pIpArray ) /*++ Routine Description: Print IP address array struct Just pass through to more generic print routine. --*/ { // protect against NULL case if ( !pIpArray ) { PrintRoutine( pContext, "%s\tNULL IP Array.\r\n", pszHeader ? pszHeader : "" ); } // call uncoupled IP array routine else { DnsPrint_IpAddressArray( PrintRoutine, pContext, pszHeader, pszName, pIpArray->AddrCount, pIpArray->AddrArray ); } } VOID DnsPrint_DnsAddrArray( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_ADDR_ARRAY pAddrArray ) /*++ Routine Description: Print IP address info array. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Addr Array"; } if ( !pAddrArray ) { PrintRoutine( pContext, "%s NULL AddrArray ptr.\r\n", pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" " Ptr = %p\r\n" " Count = %d\r\n", pszHeader ? pszHeader : "IP Address Info", pAddrArray, pAddrArray->AddrCount ); // // print array up to count // // DCR: IP6 address info blob // for( i=0; iAddrCount; i++ ) { IP_ADDRESS ip = pAddrArray->AddrArray[i].IpAddr; IP_ADDRESS subnetMask = pAddrArray->AddrArray[i].SubnetMask; PrintRoutine( pContext, "\tIP Address[%d] => %d.%d.%d.%d\r\n" "\tSubnetMask[%d] => %d.%d.%d.%d\r\n", i, (ip & 0xff), ((ip>>8) & 0xff), ((ip>>16) & 0xff), ((ip>>24) & 0xff), i, (subnetMask & 0xff), ((subnetMask>>8) & 0xff), ((subnetMask>>16) & 0xff), ((subnetMask>>24) & 0xff) ); } DnsPrint_Unlock(); } VOID DnsPrint_Ip6Address( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PIP6_ADDRESS pIp6Address, IN PSTR pszTrailer ) /*++ Routine Description: Print IP6 address. Arguments: PrintRoutine -- print routine to call pContext -- first argument to print routine pszHeader -- header to print NOTE: unlike other print routines this routine requires header to contain newline,tab, etc if multiline print is desired; the reason is to allow use of this routine for single line print pIp6Address -- ptr to IP6 address to print pszTrailer -- trailer to print NOTE: again this routine is designed to allow single line print; if newline required after print, send newline in trailer Return Value: Ptr to next location in buffer (the terminating NULL). --*/ { CHAR buffer[ IP6_ADDRESS_STRING_BUFFER_LENGTH ]; if ( !pszHeader ) { pszHeader = "IP6 Address: "; } if ( !pszTrailer ) { pszHeader = "\r\n"; } if ( !pIp6Address ) { PrintRoutine( pContext, "%s NULL IP6 address ptr.%s", pszHeader, pszTrailer ); } // convert IP6 address to string Dns_Ip6AddressToString_A( buffer, pIp6Address ); PrintRoutine( pContext, "%s%s%s", pszHeader, buffer, pszTrailer ); } // // Print routines for DNS types and structures // INT Dns_WritePacketNameToBuffer( OUT PCHAR pBuffer, OUT PCHAR * ppBufferOut, IN PBYTE pMsgName, IN PDNS_HEADER pMsgHead, OPTIONAL IN PBYTE pMsgEnd OPTIONAL ) /*++ Routine Description: Write packet name into buffer. Arguments: pBuffer - buffer to print to, bounds checking done only on each loop to limit to twice DNS_MAX_NAME_LENGTH, recommend input buffer of at least that length ppBufferOut - ptr to terminating NULL in buffer conversion, this is position at which additional printing to buffer could resume pMsgName - ptr to name in packet to print pMsgHead - ptr to DNS message; need for offsetting, if not given names are not printed past first offset pMsgEnd - ptr to end of message, specifically byte immediately after message Return Value: Count of bytes in packet name occupied. This offset from pMsgName is the next field in the packet. Zero return indicates error in message name. --*/ { register PUCHAR pchbuf; register PUCHAR pchmsg; register UCHAR cch; PCHAR pbufStop; PCHAR pnextLabel; UCHAR compressionType; WORD offset; PCHAR pbyteAfterFirstOffset = NULL; // // no message end specified? // make it max ptr so we can do single test for ptr validity // rather than testing for pMsgEnd existence first // if ( !pMsgEnd ) { pMsgEnd = (PVOID)(INT_PTR)(-1); } // // loop until copy as printable name, or hit compression or name error // pchbuf = pBuffer; pbufStop = pchbuf + DNS_MAX_NAME_LENGTH + DNS_MAX_LABEL_LENGTH; pchmsg = pMsgName; while ( 1 ) { // bounds checking to survive bad packet // // DEVNOTE: note this is not strictly a bad packet (could be just a // heck of a lot of labels) and we could // a) let packet processing proceed without printing // or // b) require buffer that could contain max legal DNS name // but not worth the effort if ( pchbuf >= pbufStop ) { pchbuf += sprintf( pchbuf, "[ERROR name exceeds safe print buffer length]\r\n" ); pchmsg = pMsgName; break; } cch = (UCHAR) *pchmsg++; compressionType = cch & 0xC0; DNSDBG( OFF, ( "byte = (%d) (0x%02x)\r\n" "compress flag = (%d) (0x%02x)\r\n", cch, cch, compressionType, compressionType )); // // normal length byte // - write length field // - copy label to print buffer // if ( compressionType == 0 ) { pchbuf += sprintf( pchbuf, "(%d)", (INT)cch ); // terminate at root name if ( ! cch ) { break; } // check that within packet pnextLabel = pchmsg + cch; if ( pnextLabel >= pMsgEnd ) { pchbuf += sprintf( pchbuf, "[ERROR length byte: 0x%02X at %p leads outside message]\r\n", cch, pchmsg ); // force zero byte return pchmsg = pMsgName; break; } // copy label to output string memcpy( pchbuf, pchmsg, cch ); pchbuf += cch; pchmsg = pnextLabel; continue; } // // valid compression // else if ( compressionType == (UCHAR)0xC0 ) { // check that compression word not straddling message end if ( pchmsg >= pMsgEnd ) { pchbuf += sprintf( pchbuf, "[ERROR compression word at %p is outside message]\r\n", pchmsg ); // force zero byte return pchmsg = pMsgName; break; } // calculate offset offset = cch; // high byte offset <<= 8; offset |= *pchmsg++; // low byte pchbuf += sprintf( pchbuf, "[%04hX]", offset ); if ( pMsgHead ) { // // on first compression, save ptr to byte immediately after // name, so can calculate next byte // // save ptr to next byte in mess, to calculate actual length // name takes up in packet // if ( ! pbyteAfterFirstOffset ) { pbyteAfterFirstOffset = pchmsg; } // // jump to offset for continuation of name // - clear two highest bits to get length // offset = offset ^ 0xC000; DNS_ASSERT( (offset & 0xC000) == 0 ); pnextLabel = (PCHAR)pMsgHead + offset; if ( pnextLabel >= pchmsg - sizeof(WORD) ) { pchbuf += sprintf( pchbuf, "[ERROR offset at %p to higher byte in packet %p]\r\n", pchmsg - sizeof(WORD), pnextLabel ); break; } pchmsg = pnextLabel; continue; } // if no ptr to message head, can not continue at offset // NULL terminate previous label else { *pchbuf++ = 0; break; } } // // invalid compression // - force zero byte return to indicate error else { pchbuf += sprintf( pchbuf, "[ERROR length byte: 0x%02X]", cch ); pchmsg = pMsgName; break; } } // // return ptr to next position in output buffer // if ( ppBufferOut ) { *ppBufferOut = pchbuf; } // // return number of bytes read from message // if ( pbyteAfterFirstOffset ) { pchmsg = pbyteAfterFirstOffset; } return (INT)( pchmsg - pMsgName ); } INT DnsPrint_PacketName( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, OPTIONAL IN PBYTE pMsgName, IN PDNS_HEADER pMsgHead, OPTIONAL IN PBYTE pMsgEnd, OPTIONAL IN PSTR pszTrailer OPTIONAL ) /*++ Routine Description: Print DNS name given in packet format. Arguments: PrintRoutine - routine to print with pszHeader - header to print pMsgHead - ptr to DNS message; need for offsetting, if not given names are not printed past first offset pMsgName - ptr to name in packet to print pMsgEnd - ptr to end of message; OPTIONAL, but needed to protect against AV accessing bad packet names pszTrailer - trailer to print after name Return Value: Count of bytes in packet name occupied. This offset from pMsgName is the next field in the packet. Zero return indicates error in message name. --*/ { INT countNameBytes; // name buffer, allow space for full name, plus parens on length // fields plus several compression flags CHAR PrintName[ 2*DNS_MAX_NAME_LENGTH ]; if ( ! pMsgName ) { PrintRoutine( pContext, "%s(NULL packet name ptr)%s\r\n", pszHeader ? pszHeader : "", pszTrailer ? pszTrailer : "" ); return 0; } // // build packet name into buffer, then print // countNameBytes = Dns_WritePacketNameToBuffer( PrintName, NULL, pMsgName, pMsgHead, pMsgEnd ); PrintRoutine( pContext, "%s%s%s", pszHeader ? pszHeader : "", PrintName, pszTrailer ? pszTrailer : "" ); return( countNameBytes ); } VOID DnsPrint_Message( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_MSG_BUF pMsg ) /*++ Routine Description: Print DNS message buffer. Includes context information as well as actual DNS message. --*/ { PDNS_HEADER pmsgHeader; PCHAR pchRecord; PBYTE pmsgEnd; INT i; INT isection; INT cchName; WORD wLength; WORD wOffset; WORD wXid; WORD wQuestionCount; WORD wAnswerCount; WORD wNameServerCount; WORD wAdditionalCount; WORD countSectionRR; BOOL fFlipped = FALSE; DnsPrint_Lock(); if ( pszHeader ) { PrintRoutine( pContext, "%s\r\n", pszHeader ); } // get message info // // note: length may not be correctly set while building message, // so make pmsgEnd greater of given length and pCurrent ptr // but allow for case where set back to pre-OPT length // wLength = pMsg->MessageLength; pmsgHeader = &pMsg->MessageHead; pmsgEnd = ((PBYTE)pmsgHeader) + wLength; if ( pmsgEnd < pMsg->pCurrent && pmsgEnd != pMsg->pPreOptEnd ) { pmsgEnd = pMsg->pCurrent; } // // print header info // PrintRoutine( pContext, "%s %s info at %p\r\n" " Socket = %d\r\n" " Remote addr %s, port %ld\r\n" " Buf length = 0x%04x\r\n" " Msg length = 0x%04x\r\n" " Message:\r\n", ( pMsg->fTcp ? "TCP" : "UDP" ), ( pmsgHeader->IsResponse ? "response" : "question" ), pMsg, pMsg->Socket, MSG_REMOTE_IP_STRING( pMsg ), MSG_REMOTE_IP_PORT( pMsg ), pMsg->BufferLength, wLength ); PrintRoutine( pContext, " XID 0x%04hx\r\n" " Flags 0x%04hx\r\n" " QR 0x%lx (%s)\r\n" " OPCODE 0x%lx (%s)\r\n" " AA 0x%lx\r\n" " TC 0x%lx\r\n" " RD 0x%lx\r\n" " RA 0x%lx\r\n" " Z 0x%lx\r\n" " RCODE 0x%lx (%s)\r\n" " QCOUNT 0x%hx\r\n" " ACOUNT 0x%hx\r\n" " NSCOUNT 0x%hx\r\n" " ARCOUNT 0x%hx\r\n", pmsgHeader->Xid, ntohs((*((PWORD)pmsgHeader + 1))), pmsgHeader->IsResponse, (pmsgHeader->IsResponse ? "response" : "question"), pmsgHeader->Opcode, Dns_OpcodeString( pmsgHeader->Opcode ), pmsgHeader->Authoritative, pmsgHeader->Truncation, pmsgHeader->RecursionDesired, pmsgHeader->RecursionAvailable, pmsgHeader->Reserved, pmsgHeader->ResponseCode, Dns_ResponseCodeString( pmsgHeader->ResponseCode ), pmsgHeader->QuestionCount, pmsgHeader->AnswerCount, pmsgHeader->NameServerCount, pmsgHeader->AdditionalCount ); // // determine if byte flipped and get correct count // wXid = pmsgHeader->Xid; wQuestionCount = pmsgHeader->QuestionCount; wAnswerCount = pmsgHeader->AnswerCount; wNameServerCount = pmsgHeader->NameServerCount; wAdditionalCount = pmsgHeader->AdditionalCount; if ( wQuestionCount ) { fFlipped = wQuestionCount & 0xff00; } else if ( wNameServerCount ) { fFlipped = wNameServerCount & 0xff00; } if ( fFlipped ) { wXid = ntohs( wXid ); wQuestionCount = ntohs( wQuestionCount ); wAnswerCount = ntohs( wAnswerCount ); wNameServerCount = ntohs( wNameServerCount ); wAdditionalCount = ntohs( wAdditionalCount ); } // // catch record flipping problems -- all are flipped or none at all // and no record count should be > 256 EXCEPT answer count // during FAST zone transfer // DNS_ASSERT( ! (wQuestionCount & 0xff00) ); DNS_ASSERT( ! (wNameServerCount & 0xff00) ); DNS_ASSERT( ! (wAdditionalCount & 0xff00) ); #if 0 // // stop here if WINS response -- don't have parsing ready // if ( pmsgHeader->IsResponse && IS_WINS_XID(wXid) ) { PrintRoutine( pContext, " WINS Response packet.\r\n\r\n" ); goto Unlock; } #endif // // print questions and resource records // pchRecord = (PCHAR)(pmsgHeader + 1); for ( isection=0; isection<4; isection++) { PrintRoutine( pContext, " %s Section:\r\n", Dns_SectionNameString( isection, pmsgHeader->Opcode ) ); if ( isection==0 ) { countSectionRR = wQuestionCount; } else if ( isection==1 ) { countSectionRR = wAnswerCount; } else if ( isection==2 ) { countSectionRR = wNameServerCount; } else if ( isection==3 ) { countSectionRR = wAdditionalCount; } for ( i=0; i < countSectionRR; i++ ) { // // verify not overrunning length // - check against pCurrent as well as message length // so can print packets while being built // wOffset = (WORD)(pchRecord - (PCHAR)pmsgHeader); if ( wOffset >= wLength && pchRecord >= pMsg->pCurrent ) { PrintRoutine( pContext, "ERROR: BOGUS PACKET:\r\n" "\tFollowing RR (offset %d) past packet length (%d).\r\n", wOffset, wLength ); goto Unlock; } // // print RR name // PrintRoutine( pContext, " Name Offset = 0x%04x\r\n", wOffset ); cchName = DnsPrint_PacketName( PrintRoutine, pContext, " Name \"", pchRecord, pmsgHeader, pmsgEnd, "\"\r\n" ); if ( ! cchName ) { PrintRoutine( pContext, "ERROR: Invalid name length, stop packet print\r\n" ); DNS_ASSERT( FALSE ); break; } pchRecord += cchName; // print question or resource record if ( isection == 0 ) { PrintRoutine( pContext, " QTYPE %d\r\n" " QCLASS %d\r\n", FlipUnalignedWord( pchRecord ), FlipUnalignedWord( pchRecord + sizeof(WORD) ) ); pchRecord += sizeof( DNS_WIRE_QUESTION ); } else { pchRecord += DnsPrint_PacketRecord( PrintRoutine, pContext, NULL, (PDNS_WIRE_RECORD) pchRecord, pmsgHeader, pmsgEnd ); } } } // check that at proper end of packet wOffset = (WORD)(pchRecord - (PCHAR)pmsgHeader); if ( pchRecord < pMsg->pCurrent || wOffset < wLength ) { PrintRoutine( pContext, "WARNING: message continues beyond these records\r\n" "\tpch = %p, pCurrent = %p, %d bytes\r\n" "\toffset = %d, msg length = %d, %d bytes\r\n", pchRecord, pMsg->pCurrent, pMsg->pCurrent - pchRecord, wOffset, wLength, wLength - wOffset ); } PrintRoutine( pContext, " Message length = %04x\n\r\n", wOffset ); Unlock: DnsPrint_Unlock(); } // DnsPrint_Message INT DnsPrint_PacketRecord( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_WIRE_RECORD pMsgRR, IN PDNS_HEADER pMsgHead, OPTIONAL IN PBYTE pMsgEnd OPTIONAL ) /*++ Routine Description: Print RR in packet format. Arguments: pszHeader - Header message/name for RR. pMsgRR - resource record to print pMsgHead - ptr to DNS message; need for offsetting, if not given names are not printed past first offset pMsgEnd - ptr to end of message, specifically byte immediately after message Return Value: Number of bytes in record. --*/ { PCHAR pdata = (PCHAR)(pMsgRR + 1); PCHAR pdataStop; WORD dlen = FlipUnalignedWord( &pMsgRR->DataLength ); WORD type; PCHAR pRRString; DnsPrint_Lock(); // // print RR fixed fields // type = FlipUnalignedWord( &pMsgRR->RecordType ); pRRString = Dns_RecordStringForType( type ); if ( pszHeader ) { PrintRoutine( pContext, "%s\r\n", pszHeader ); } PrintRoutine( pContext, " TYPE %s (%u)\r\n" " CLASS %u\r\n" " TTL %lu\r\n" " DLEN %u\r\n" " DATA ", pRRString, type, FlipUnalignedWord( &pMsgRR->RecordClass ), FlipUnalignedDword( &pMsgRR->TimeToLive ), dlen ); // // update records may not have data // if ( dlen == 0 ) { PrintRoutine( pContext, "(none)\r\n" ); goto Done; } // stop byte after RR data pdataStop = pdata + dlen; if ( pMsgEnd < pdataStop ) { PrintRoutine( pContext, "ERROR: record at %p extends past end of packet!\n" "\tpmsg = %p\n" "\tpmsgEnd = %p\n" "\trecord end = %p\n", pMsgRR, pMsgHead, pMsgEnd, pdataStop ); goto Done; } // // print RR data // switch ( type ) { case DNS_TYPE_A: PrintRoutine( pContext, "%d.%d.%d.%d\r\n", * (PUCHAR)( pdata + 0 ), * (PUCHAR)( pdata + 1 ), * (PUCHAR)( pdata + 2 ), * (PUCHAR)( pdata + 3 ) ); break; case DNS_TYPE_AAAA: { IP6_ADDRESS ip6; RtlCopyMemory( &ip6, pdata, sizeof(ip6) ); PrintRoutine( pContext, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", ip6.IP6Word[0], ip6.IP6Word[1], ip6.IP6Word[2], ip6.IP6Word[3], ip6.IP6Word[4], ip6.IP6Word[5], ip6.IP6Word[6], ip6.IP6Word[7] ); break; } case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: case DNS_TYPE_MD: case DNS_TYPE_MB: case DNS_TYPE_MF: case DNS_TYPE_MG: case DNS_TYPE_MR: // // these RRs contain single domain name // DnsPrint_PacketName( PrintRoutine, pContext, NULL, pdata, pMsgHead, pMsgEnd, "\r\n" ); break; case DNS_TYPE_MX: case DNS_TYPE_RT: case DNS_TYPE_AFSDB: // // these RR contain // - one preference value // - one domain name // PrintRoutine( pContext, "%d ", FlipUnalignedWord( pdata ) ); DnsPrint_PacketName( PrintRoutine, pContext, NULL, pdata + sizeof(WORD), pMsgHead, pMsgEnd, "\r\n" ); break; case DNS_TYPE_SOA: pdata += DnsPrint_PacketName( PrintRoutine, pContext, "\r\n\t\tPrimaryServer: ", pdata, pMsgHead, pMsgEnd, NULL ); pdata += DnsPrint_PacketName( PrintRoutine, pContext, "\r\n\t\tAdministrator: ", pdata, pMsgHead, pMsgEnd, "\r\n" ); PrintRoutine( pContext, "\t\tSerialNo = %d\r\n" "\t\tRefresh = %d\r\n" "\t\tRetry = %d\r\n" "\t\tExpire = %d\r\n" "\t\tMinimumTTL = %d\r\n", FlipUnalignedDword( pdata ), FlipUnalignedDword( (PDWORD)pdata+1 ), FlipUnalignedDword( (PDWORD)pdata+2 ), FlipUnalignedDword( (PDWORD)pdata+3 ), FlipUnalignedDword( (PDWORD)pdata+4 ) ); break; case DNS_TYPE_MINFO: case DNS_TYPE_RP: // // these RRs contain two domain names // pdata += DnsPrint_PacketName( PrintRoutine, pContext, NULL, pdata, pMsgHead, pMsgEnd, NULL ); DnsPrint_PacketName( PrintRoutine, pContext, " ", pdata, pMsgHead, pMsgEnd, "\r\n" ); break; case DNS_TYPE_TEXT: case DNS_TYPE_HINFO: case DNS_TYPE_ISDN: case DNS_TYPE_X25: { // // all these are simply text string(s) // PCHAR pch = pdata; PCHAR pchStop = pch + dlen; UCHAR cch; while ( pch < pchStop ) { cch = (UCHAR) *pch++; PrintRoutine( pContext, "\t%.*s\r\n", cch, pch ); pch += cch; } if ( pch != pchStop ) { PrintRoutine( pContext, "ERROR: Bad RR. " "Text strings do not add to RR length.\r\n" ); } break; } case DNS_TYPE_WKS: { INT i; PrintRoutine( pContext, "WKS: Address %d.%d.%d.%d\r\n" "\t\tProtocol %d\r\n" "\t\tBitmask\r\n", * (PUCHAR)( pdata + 0 ), * (PUCHAR)( pdata + 1 ), * (PUCHAR)( pdata + 2 ), * (PUCHAR)( pdata + 3 ), * (PUCHAR)( pdata + 4 ) ); pdata += SIZEOF_WKS_FIXED_DATA; for ( i=0; i < (INT)(dlen-SIZEOF_WKS_FIXED_DATA); i++ ) { PrintRoutine( pContext, "\t\t\tbyte[%d] = %x\r\n", i, (UCHAR) pdata[i] ); } break; } case DNS_TYPE_NULL: DnsPrint_RawOctets( PrintRoutine, pContext, NULL, "\t\t", pdata, dlen ); break; case DNS_TYPE_SRV: // SRV PrintRoutine( pContext, "\t\tPriority = %d\r\n" "\t\tWeight = %d\r\n" "\t\tPort = %d\r\n", FlipUnalignedWord( pdata ), FlipUnalignedWord( (PWORD)pdata+1 ), FlipUnalignedWord( (PWORD)pdata+2 ) ); DnsPrint_PacketName( PrintRoutine, pContext, "\t\tTarget host ", pdata + 3*sizeof(WORD), pMsgHead, pMsgEnd, "\r\n" ); break; case DNS_TYPE_OPT: // // OPT // - RR class is buffer size // - RR TTL contains // low byte // second byte // high word // { BYTE version; BYTE extendedRcode; DWORD fullRcode = 0; WORD flags; extendedRcode = *( (PBYTE) &pMsgRR->TimeToLive ); version = *( (PBYTE) &pMsgRR->TimeToLive + 1 ); flags = *( (PWORD) &pMsgRR->TimeToLive + 1 ); if ( pMsgHead->ResponseCode ) { fullRcode = ((DWORD)extendedRcode << 4) + (DWORD)pMsgHead->ResponseCode; } PrintRoutine( pContext, "\t\tBuffer Size = %d\r\n" "\t\tRcode Ext = %d (%x)\r\n" "\t\tRcode Full = %d\r\n" "\t\tVersion = %d\r\n" "\t\tFlags = %x\r\n", FlipUnalignedWord( &pMsgRR->RecordClass ), extendedRcode, extendedRcode, fullRcode, version, flags ); } break; case DNS_TYPE_TKEY: { DWORD beginTime; DWORD expireTime; WORD keyLength; WORD mode; WORD extRcode; WORD otherLength; otherLength = (WORD)DnsPrint_PacketName( PrintRoutine, pContext, "\r\n\t\tAlgorithm: ", pdata, pMsgHead, pMsgEnd, NULL ); if ( !otherLength ) { PrintRoutine( pContext, "Invalid algorithm name in TKEY RR!\r\n" ); } pdata += otherLength; beginTime = InlineFlipUnalignedDword( pdata ); pdata += sizeof(DWORD); expireTime = InlineFlipUnalignedDword( pdata ); pdata += sizeof(DWORD); mode = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); extRcode = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); keyLength = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); PrintRoutine( pContext, "\r\n" "\t\tCreate time = %d\r\n" "\t\tExpire time = %d\r\n" "\t\tMode = %d\r\n" "\t\tExtended RCODE = %d\r\n" "\t\tKey Length = %d\r\n", beginTime, expireTime, mode, extRcode, keyLength ); if ( pdata + keyLength > pdataStop ) { PrintRoutine( pContext, "Invalid key length: exceeds record data!\r\n" ); break; } DnsPrint_RawOctets( PrintRoutine, pContext, "\t\tKey:", "\t\t ", // line header pdata, keyLength ); pdata += keyLength; if ( pdata + sizeof(WORD) > pdataStop ) { break; } otherLength = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); PrintRoutine( pContext, "\r\n" "\t\tOther Length = %d\r\n", otherLength ); if ( pdata + otherLength > pdataStop ) { PrintRoutine( pContext, "Invalid other data length: exceeds record data!\r\n" ); break; } DnsPrint_RawOctets( PrintRoutine, pContext, "\t\tOther Data:", "\t\t ", // line header pdata, otherLength ); break; } case DNS_TYPE_TSIG: { ULONGLONG signTime; WORD fudgeTime; WORD sigLength; WORD extRcode; WORD wOriginalId; WORD otherLength; otherLength = (WORD) DnsPrint_PacketName( PrintRoutine, pContext, "\r\n\t\tAlgorithm: ", pdata, pMsgHead, pMsgEnd, NULL ); if ( !otherLength ) { PrintRoutine( pContext, "Invalid algorithm name in TSIG RR!\r\n" ); } pdata += otherLength; signTime = InlineFlipUnaligned48Bits( pdata ); pdata += sizeof(DWORD) + sizeof(WORD); fudgeTime = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); sigLength = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); PrintRoutine( pContext, "\r\n" "\t\tSigned time = %I64u\r\n" "\t\tFudge time = %u\r\n" "\t\tSig Length = %u\r\n", signTime, fudgeTime, sigLength ); if ( pdata + sigLength > pdataStop ) { PrintRoutine( pContext, "Invalid signature length: exceeds record data!\r\n" ); break; } DnsPrint_RawOctets( PrintRoutine, pContext, "\t\tSignature:", "\t\t ", // line header pdata, sigLength ); pdata += sigLength; if ( pdata + sizeof(DWORD) > pdataStop ) { break; } wOriginalId = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); extRcode = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); otherLength = InlineFlipUnalignedWord( pdata ); pdata += sizeof(WORD); PrintRoutine( pContext, "\r\n" "\t\tOriginal XID = %x\r\n" "\t\tExtended RCODE = %u\r\n" "\t\tOther Length = %u\r\n", wOriginalId, extRcode, otherLength ); if ( pdata + otherLength > pdataStop ) { PrintRoutine( pContext, "Invalid other data length: exceeds record data!\r\n" ); break; } DnsPrint_RawOctets( PrintRoutine, pContext, "\t\tOther Data:", "\t\t ", // line header pdata, otherLength ); break; } case DNS_TYPE_WINS: { DWORD i; DWORD winsFlags; DWORD lookupTimeout; DWORD cacheTimeout; DWORD winsCount; CHAR flagString[ WINS_FLAG_MAX_LENGTH ]; // // WINS // - scope/domain mapping flag // - lookup timeout // - cache timeout // - WINS server count // - WINS server list // winsFlags = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); lookupTimeout = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); cacheTimeout = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); winsCount = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); Dns_WinsRecordFlagString( winsFlags, flagString ); PrintRoutine( pContext, "\r\n" "\t\tWINS flags = %s (%08x)\r\n" "\t\tLookup timeout = %d\r\n" "\t\tCaching TTL = %d\r\n", flagString, winsFlags, lookupTimeout, cacheTimeout ); if ( pdata + (winsCount * SIZEOF_IP_ADDRESS) > pdataStop ) { PrintRoutine( pContext, "ERROR: WINS server count leads beyond record data length!\n" "\tpmsg = %p\n" "\tpmsgEnd = %p\n" "\tpRR = %p\n" "\trecord data end = %p\n" "\twins count = %d\n" "\tend of wins IPs = %p\n", pMsgHead, pMsgEnd, pMsgRR, pdataStop, winsCount, pdata + (winsCount * SIZEOF_IP_ADDRESS) ); goto Done; } DnsPrint_IpAddressArray( PrintRoutine, pContext, NULL, "\tWINS", winsCount, (PIP_ADDRESS) pdata ); break; } case DNS_TYPE_WINSR: { DWORD winsFlags; DWORD lookupTimeout; DWORD cacheTimeout; CHAR flagString[ WINS_FLAG_MAX_LENGTH ]; // // NBSTAT // - scope/domain mapping flag // - lookup timeout // - cache timeout // - result domain -- optional // winsFlags = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); lookupTimeout = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); cacheTimeout = FlipUnalignedDword( pdata ); pdata += sizeof(DWORD); Dns_WinsRecordFlagString( winsFlags, flagString ); PrintRoutine( pContext, "\r\n" "\t\tWINS-R flags = %s (%08x)\r\n" "\t\tLookup timeout = %d\r\n" "\t\tCaching TTL = %d\r\n", flagString, winsFlags, lookupTimeout, cacheTimeout ); DnsPrint_PacketName( PrintRoutine, pContext, "\t\tResult domain = ", pdata, pMsgHead, pMsgEnd, "\r\n" ); break; } case DNS_TYPE_KEY: { WORD flags; BYTE protocol; BYTE algorithm; INT keyLength; CHAR szKeyFlags[ 100 ]; keyLength = dlen - SIZEOF_KEY_FIXED_DATA; flags = FlipUnalignedWord( pdata ); pdata += sizeof( WORD ); protocol = * ( PBYTE ) pdata; ++pdata; algorithm = * ( PBYTE ) pdata; ++pdata; PrintRoutine( pContext, "\r\n" "\t\tKEY flags = 0x%04x %s\r\n" "\t\tKEY protocol = %s (%d)\r\n" "\t\tKEY algorithm = %s (%d)\r\n", (INT) flags, Dns_KeyFlagString( szKeyFlags, flags ), Dns_GetKeyProtocolString( protocol ), (INT) protocol, Dns_GetDnssecAlgorithmString( algorithm ), (INT) algorithm ); DnsPrint_RawOctets( PrintRoutine, pContext, "\t\tPublic key:", "\t\t ", // line header pdata, keyLength ); break; } case DNS_TYPE_SIG: { WORD typeCovered; BYTE algorithm; BYTE labelCount; DWORD originalTTL; DWORD sigInception; DWORD sigExpiration; WORD keyTag; CHAR szSigInception[ 100 ]; CHAR szSigExpiration[ 100 ]; INT sigLength; typeCovered = FlipUnalignedWord( pdata ); pdata += sizeof( WORD ); algorithm = * ( PBYTE ) pdata; ++pdata; labelCount = * ( PBYTE ) pdata; ++pdata; originalTTL = FlipUnalignedDword( pdata ); pdata += sizeof( DWORD ); sigExpiration = FlipUnalignedDword( pdata ); pdata += sizeof( DWORD ); sigInception = FlipUnalignedDword( pdata ); pdata += sizeof( DWORD ); keyTag = FlipUnalignedWord( pdata ); pdata += sizeof( WORD ); PrintRoutine( pContext, "\r\n" "\t\tSIG type covered = %s\r\n" "\t\tSIG algorithm = %s (%d)\r\n" "\t\tSIG label count = %d\r\n" "\t\tSIG original TTL = %d\r\n" "\t\tSIG expiration = %s\r\n" "\t\tSIG inception = %s\r\n" "\t\tSIG key tag = %d\r\n", Dns_RecordStringForType( typeCovered ), Dns_GetDnssecAlgorithmString( ( BYTE ) algorithm ), ( INT ) algorithm, ( INT ) labelCount, ( INT ) originalTTL, Dns_SigTimeString( sigExpiration, szSigExpiration ), Dns_SigTimeString( sigInception, szSigInception ), ( INT ) keyTag ); pdata += DnsPrint_PacketName( PrintRoutine, pContext, "\t\tSIG signer's name = ", pdata, pMsgHead, pMsgEnd, "\r\n" ); sigLength = ( INT ) ( pdataStop - pdata ); DnsPrint_RawOctets( PrintRoutine, pContext, "\t\tSignature:", "\t\t ", // line header pdata, sigLength ); break; } case DNS_TYPE_NXT: { INT bitmapLength; INT byteIdx; INT bitIdx; pdata += DnsPrint_PacketName( PrintRoutine, pContext, "\r\n\t\tNXT next name = ", pdata, pMsgHead, pMsgEnd, "\r\n" ); bitmapLength = ( INT ) ( pdataStop - pdata ); PrintRoutine( pContext, "\t\tNXT types covered = " ); for ( byteIdx = 0; byteIdx < bitmapLength; ++byteIdx ) { for ( bitIdx = ( byteIdx ? 0 : 1 ); bitIdx < 8; ++bitIdx ) { PCHAR pszType; if ( !( pdata[ byteIdx ] & ( 1 << bitIdx ) ) ) { continue; // Bit value is zero - do not write string. } pszType = Dns_RecordStringForType( byteIdx * 8 + bitIdx ); if ( !pszType ) { ASSERT( FALSE ); continue; // This type has no string - do not write. } PrintRoutine( pContext, "%s ", pszType ); } } PrintRoutine( pContext, "\r\n" ); break; } default: PrintRoutine( pContext, "Unknown resource record type %d at %p.\r\n", type, pMsgRR ); DnsPrint_RawOctets( PrintRoutine, pContext, NULL, "\t\t", pdata, dlen ); break; } Done: DnsPrint_Unlock(); return( sizeof(DNS_WIRE_RECORD) + dlen ); } // // Print related utilities // INT Dns_WriteFormattedSystemTimeToBuffer( OUT PCHAR pBuffer, IN PSYSTEMTIME pSystemTime ) /*++ Routine Description: Write SYSTEMTIME structure to buffer. Arguments: pBuffer -- buffer to write into, assumed to have at least 50 bytes available pSystemTime -- system time to convert; assumed to be local, no time zone conversion is done Return Value: Bytes in formatted string. --*/ { PCHAR pend = pBuffer + 60; PCHAR pstart = pBuffer; INT count; pBuffer += GetDateFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, (PSYSTEMTIME) pSystemTime, NULL, pBuffer, (int)(pend - pBuffer) ); // Replace NULL from GetDateFormat with a space. *( pBuffer - 1 ) = ' '; pBuffer += GetTimeFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, (PSYSTEMTIME) pSystemTime, NULL, pBuffer, (int)(pend - pBuffer) ); if ( pBuffer <= pstart+1 ) { return( 0 ); } return (INT)( pBuffer - pstart ); } // // Response code print // #define DNS_RCODE_UNKNOWN (DNS_RCODE_BADTIME + 1) PCHAR ResponseCodeStringTable[] = { "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH", "NOTZONE", "11 - unknown\r\n", "12 - unknown\r\n", "13 - unknown\r\n", "14 - unknown\r\n", "15 - unknown\r\n", // DNS RCODEs stop at 15 -- these extended errors are available for security "BADSIG", "BADKEY", "BADTIME", "UNKNOWN" }; PCHAR Dns_ResponseCodeString( IN INT ResponseCode ) /*++ Routine Description: Get string corresponding to a response code. Arguments: ResponseCode - response code Return Value: Ptr to string for code. --*/ { if ( ResponseCode > DNS_RCODE_UNKNOWN ) { ResponseCode = DNS_RCODE_UNKNOWN; } return( ResponseCodeStringTable[ ResponseCode ] ); } // // More detailed RCODE strings // PCHAR ResponseCodeExplanationStringTable[] = { "NOERROR: no error", "FORMERR: format error", "SERVFAIL: server failure", "NXDOMAIN: name error", "NOTIMP: not implemented", "REFUSED", "YXDOMAIN: name exists that should not", "YXRRSET: RR set exists that should not", "NXRRSET: required RR set does not exist", "NOTAUTH: not authoritative", "NOTZONE: name not in zone", "11 - unknown", "12 - unknown", "13 - unknown", "14 - unknown", "15 - unknown", // DNS RCODEs stop at 15 -- these extended errors are available for security "BADSIG: bad signature", "BADKEY: bad signature", "BADTIME: invalid or expired time on signature or key", "UNKNOWN" }; PCHAR Dns_ResponseCodeExplanationString( IN INT ResponseCode ) /*++ Routine Description: Get string corresponding to a response code. Basically for use by packet debug routine above. Arguments: ResponseCode - response code Return Value: Ptr to string for code. --*/ { if ( ResponseCode > DNS_RCODE_UNKNOWN ) { ResponseCode = DNS_RCODE_UNKNOWN; } return( ResponseCodeExplanationStringTable[ ResponseCode ] ); } PCHAR Dns_KeyFlagString( IN OUT PCHAR pszBuff, IN WORD Flags ) /*++ Routine Description: Formats a human-readable string based on the flags value (DNSSEC KEY RR flags). See RFC2535 section 3.2.1. Arguments: pszBuff - buffer to dump string into should be min 100 chars flags - flag value to generate string for Return Value: pszBuff --*/ { BOOL fZoneKey = FALSE; *pszBuff = '\0'; // "type" bits if ( ( Flags & 0xC000 ) == 0xC000 ) { strcat( pszBuff, "NOKEY " ); } else if ( ( Flags & 0xC000 ) == 0x8000 ) { strcat( pszBuff, "NOAUTH " ); } else if ( ( Flags & 0xC000 ) == 0x4000 ) { strcat( pszBuff, "NOCONF " ); } else { strcat( pszBuff, "NOAUTH NOCONF " ); } // extended bit if ( Flags & 0x1000 ) { strcat( pszBuff, "EXTEND " ); } // name type bits if ( ( Flags & 0x0300 ) == 0x0300 ) { strcat( pszBuff, "RESNT " ); // reserved name type } else if ( ( Flags & 0x0200 ) == 0x0100 ) { strcat( pszBuff, "ENTITY " ); } else if ( ( Flags & 0x0100 ) == 0x4000 ) { strcat( pszBuff, "ZONE " ); fZoneKey = TRUE; } else { strcat( pszBuff, "USER " ); } // signatory bits if ( fZoneKey ) { strcat( pszBuff, ( Flags & 0x0008 ) ? "MODEA " : "MODEB " ); if ( Flags & 0x0004 ) { strcat( pszBuff, "STRONG " ); } if ( Flags & 0x0002 ) { strcat( pszBuff, "UNIQ " ); } } else { if ( Flags & 0x0008 ) { strcat( pszBuff, "ZCTRL " ); } if ( Flags & 0x0004 ) { strcat( pszBuff, "STRONG " ); } if ( Flags & 0x0002 ) { strcat( pszBuff, "UNIQ " ); } } return pszBuff; } // // Opcode print // PCHAR OpcodeStringTable[] = { "QUERY", "IQUERY", "SRV_STATUS", "UNKNOWN", "NOTIFY", "UPDATE", "UNKNOWN?" }; CHAR OpcodeCharacterTable[] = { 'Q', 'I', 'S', 'K', 'N', 'U', '?' }; #define DNS_OPCODE_UNSPEC (DNS_OPCODE_UPDATE + 1) PCHAR Dns_OpcodeString( IN INT Opcode ) /*++ Routine Description: Get string corresponding to a response code. Arguments: Opcode - response code Return Value: Ptr to string for code. --*/ { if ( Opcode > DNS_OPCODE_UNSPEC ) { Opcode = DNS_OPCODE_UNSPEC; } return( OpcodeStringTable[ Opcode ] ); } CHAR Dns_OpcodeCharacter( IN INT Opcode ) /*++ Routine Description: Get string corresponding to an opcode. Arguments: Opcode - response code Return Value: Ptr to string for code. --*/ { if ( Opcode > DNS_OPCODE_UNSPEC ) { Opcode = DNS_OPCODE_UNSPEC; } return( OpcodeCharacterTable[ Opcode ] ); } // // Section names // // With update get a new set of section names. // Provide single interface to putting a name on them. // PSTR SectionNameArray[5] = { "Question", "Answer", "Authority", "Additional", "ERROR: Invalid Section" }; PSTR UpdateSectionNameArray[5] = { "Zone", "Prerequisite", "Update", "Additional", "ERROR: Invalid Section" }; PCHAR Dns_SectionNameString( IN INT iSection, IN INT iOpcode ) /*++ Routine Description: Get string corresponding to name of RR section id. For use by packet debug routine above. Arguments: iSection - section id (0-3 for Question-Additional) iOpcode - opcode Return Value: Ptr to string for section name. --*/ { if ( iSection >= 4 ) { iSection = 4; } if ( iOpcode == DNS_OPCODE_UPDATE ) { return( UpdateSectionNameArray[iSection] ); } else { return( SectionNameArray[iSection] ); } } VOID DnsPrint_MessageNoContext( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_HEADER pMsgHead, IN WORD wLength OPTIONAL ) /*++ Routine Description: Print DNS message buffer. Includes context information as well as actual DNS message. --*/ { PCHAR pchRecord; PBYTE pmsgEnd; INT i; INT isection; INT cchName; WORD wOffset; WORD wXid; WORD wQuestionCount; WORD wAnswerCount; WORD wNameServerCount; WORD wAdditionalCount; WORD countSectionRR; BOOL fFlipped = FALSE; // // processing limits // - if length given set stop limit // - if length not given set wLength so checks against // length overrun always fail (are ok) // if ( wLength ) { pmsgEnd = ((PBYTE)pMsgHead) + wLength; } else { wLength = MAXWORD; pmsgEnd = NULL; } DnsPrint_Lock(); if ( pszHeader ) { PrintRoutine( pContext, "%s\r\n", pszHeader ); } PrintRoutine( pContext, "DNS message header at %p\r\n", pMsgHead ); PrintRoutine( pContext, " XID 0x%04hx\r\n" " Flags 0x%04hx\r\n" " QR 0x%lx (%s)\r\n" " OPCODE 0x%lx (%s)\r\n" " AA 0x%lx\r\n" " TC 0x%lx\r\n" " RD 0x%lx\r\n" " RA 0x%lx\r\n" " Z 0x%lx\r\n" " RCODE 0x%lx (%s)\r\n" " QCOUNT 0x%hx\r\n" " ACOUNT 0x%hx\r\n" " NSCOUNT 0x%hx\r\n" " ARCOUNT 0x%hx\r\n", pMsgHead->Xid, ntohs((*((PWORD)pMsgHead + 1))), pMsgHead->IsResponse, (pMsgHead->IsResponse ? "response" : "question"), pMsgHead->Opcode, Dns_OpcodeString( pMsgHead->Opcode ), pMsgHead->Authoritative, pMsgHead->Truncation, pMsgHead->RecursionDesired, pMsgHead->RecursionAvailable, pMsgHead->Reserved, pMsgHead->ResponseCode, Dns_ResponseCodeString( pMsgHead->ResponseCode ), pMsgHead->QuestionCount, pMsgHead->AnswerCount, pMsgHead->NameServerCount, pMsgHead->AdditionalCount ); // // determine if byte flipped and get correct count // wXid = pMsgHead->Xid; wQuestionCount = pMsgHead->QuestionCount; wAnswerCount = pMsgHead->AnswerCount; wNameServerCount = pMsgHead->NameServerCount; wAdditionalCount = pMsgHead->AdditionalCount; if ( wQuestionCount ) { fFlipped = wQuestionCount & 0xff00; } else if ( wNameServerCount ) { fFlipped = wNameServerCount & 0xff00; } if ( fFlipped ) { wXid = ntohs( wXid ); wQuestionCount = ntohs( wQuestionCount ); wAnswerCount = ntohs( wAnswerCount ); wNameServerCount = ntohs( wNameServerCount ); wAdditionalCount = ntohs( wAdditionalCount ); } // // catch record flipping problems -- all are flipped or none at all // and no record count should be > 256 EXCEPT answer count // during FAST zone transfer // DNS_ASSERT( ! (wQuestionCount & 0xff00) ); DNS_ASSERT( ! (wNameServerCount & 0xff00) ); DNS_ASSERT( ! (wAdditionalCount & 0xff00) ); #if 0 // // stop here if WINS response -- don't have parsing ready // if ( pMsgHead->IsResponse && IS_WINS_XID(wXid) ) { PrintRoutine( pContext, " WINS Response packet.\r\n\r\n" ); goto Unlock; } #endif // // print questions and resource records // pchRecord = (PCHAR)(pMsgHead + 1); for ( isection=0; isection<4; isection++) { PrintRoutine( pContext, " %s Section:\r\n", Dns_SectionNameString( isection, pMsgHead->Opcode ) ); if ( isection==0 ) { countSectionRR = wQuestionCount; } else if ( isection==1 ) { countSectionRR = wAnswerCount; } else if ( isection==2 ) { countSectionRR = wNameServerCount; } else if ( isection==3 ) { countSectionRR = wAdditionalCount; } for ( i=0; i < countSectionRR; i++ ) { // // verify not overrunning length // - check against pCurrent as well as message length // so can print packets while being built // wOffset = (WORD)(pchRecord - (PCHAR)pMsgHead); if ( wOffset >= wLength ) { PrintRoutine( pContext, "ERROR: BOGUS PACKET:\r\n" "\tFollowing RR (offset %d) past packet length (%d).\r\n", wOffset, wLength ); goto Unlock; } // // print RR name // PrintRoutine( pContext, " Name Offset = 0x%04x\r\n", wOffset ); cchName = DnsPrint_PacketName( PrintRoutine, pContext, " Name \"", pchRecord, pMsgHead, pmsgEnd, "\"\r\n" ); if ( !cchName ) { PrintRoutine( pContext, "ERROR: Invalid name length, stop packet print\r\n" ); DNS_ASSERT( FALSE ); break; } pchRecord += cchName; // print question or resource record if ( isection == 0 ) { PrintRoutine( pContext, " QTYPE %d\r\n" " QCLASS %d\r\n", FlipUnalignedWord( pchRecord ), FlipUnalignedWord( pchRecord + sizeof(WORD) ) ); pchRecord += sizeof( DNS_WIRE_QUESTION ); } else { pchRecord += DnsPrint_PacketRecord( PrintRoutine, pContext, NULL, (PDNS_WIRE_RECORD) pchRecord, pMsgHead, pmsgEnd ); } } } // check that at proper end of packet wOffset = (WORD)(pchRecord - (PCHAR)pMsgHead); PrintRoutine( pContext, " Message length = %04x\r\n\r\n", wOffset ); // print warning if given message length and did not end up // at end of message // note: pmsgEnd test in case passed wLength==0, in which case // wLength set to MAXDWORD above if ( pmsgEnd && wOffset < wLength ) { PrintRoutine( pContext, "WARNING: message continues beyond these records\r\n" "\tpch = %p\r\n" "\toffset = %d, msg length = %d, %d bytes\r\n", pchRecord, wOffset, wLength, wLength - wOffset ); DnsPrint_RawOctets( PrintRoutine, pContext, "Remaining bytes:", NULL, pchRecord, (wLength - wOffset) ); } Unlock: DnsPrint_Unlock(); } // DnsPrint_MessageNoContext DWORD DnsStringPrint_Guid( OUT PCHAR pBuffer, IN PGUID pGuid ) /*++ Routine Description: Print GUID to buffer. Arguments: pBuffer - buffer to print to buffer must be big enough for GUID string GUID_STRING_BUFFER_LENGTH covers it pGuid - GUID to print Return Value: Count of bytes printed to string. --*/ { if ( !pGuid ) { *pBuffer = 0; return 0; } return sprintf( pBuffer, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", pGuid->Data1, pGuid->Data2, pGuid->Data3, *(PWORD) &pGuid->Data4[0], pGuid->Data4[2], pGuid->Data4[3], pGuid->Data4[4], pGuid->Data4[5], pGuid->Data4[6], pGuid->Data4[7], pGuid->Data4[8] ); } VOID DnsPrint_Guid( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PGUID pGuid ) /*++ Routine Description: Print GUID Arguments: pszHeader - Header message/name for RR. pGuid -- ptr to GUID to print Return Value: None. --*/ { CHAR guidBuffer[ GUID_STRING_BUFFER_LENGTH ]; if ( !pszHeader ) { pszHeader = "Guid"; } if ( !pGuid ) { PrintRoutine( pContext, "%s: NULL GUID pointer!\r\n", pszHeader ); } // convert GUID to string DnsStringPrint_Guid( guidBuffer, pGuid ); PrintRoutine( pContext, "%s: (%p) %s\r\n", pszHeader, pGuid, guidBuffer ); } DWORD DnsStringPrint_RawOctets( OUT PCHAR pBuffer, IN PCHAR pchData, IN DWORD dwLength, IN PSTR pszLineHeader, IN DWORD dwLineLength ) /*++ Routine Description: Print raw octect data to sting Arguments: pBuffer - buffer to print to pchData - data to print dwLength - length of data to print pszLineHeader - header on each line. dwLineLength - number of bytes to print on line; default is Return Value: Count of bytes printed to string. --*/ { INT i; INT lineCount = 0; PCHAR pch = pBuffer; *pch = 0; // // catch NULL pointer // - return is null terminated // - but indicate no bytes written // if ( !pchData ) { return 0; } // // write each byte in hex // - if dwLineLength set break into lines with count // or optional header // for ( i = 0; i < (INT)dwLength; i++ ) { if ( dwLineLength && (i % dwLineLength) == 0 ) { if ( pszLineHeader ) { pch += sprintf( pch, "\r\n%s", pszLineHeader ); } else { pch += sprintf( pch, "\r\n%3d> ", i ); } lineCount++; } pch += sprintf( pch, "%02x ", (UCHAR)pchData[i] ); } return( (DWORD)(pch - pBuffer) ); } VOID DnsPrint_RawBinary( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PSTR pszLineHeader, IN PCHAR pchData, IN DWORD dwLength, IN DWORD PrintSize ) /*++ Routine Description: Print raw data. Arguments: pszHeader - Header message/name for RR. pszLineHeader - Header on each line. pchData - data to print dwLength - length of data to print PrintSize - size to print in size(QWORD) size(DWORD) size(WORD) defaults to bytes Return Value: None. --*/ { DWORD i; DWORD lineCount = 0; CHAR buf[ 2000 ]; PCHAR pch = buf; PCHAR pbyte; PCHAR pend; DnsPrint_Lock(); if ( pszHeader ) { PrintRoutine( pContext, "%s", pszHeader ); } buf[0] = 0; // // print bytes // - write 16 bytes a line // - buffer up 10 lines for speed // // note: we'll write a partial (<16 byte) line the first // time if data is unaligned with PrintSize, then we'll // write at 16 a pop // if ( PrintSize == 0 ) { PrintSize = 1; } i = 0; pch = buf; pend = (PBYTE)pchData + dwLength; while ( i < dwLength ) { DWORD lineBytes = (i%16); if ( lineBytes==0 || lineBytes > (16-PrintSize) ) { if ( lineCount > 10 ) { PrintRoutine( pContext, buf ); lineCount = 0; pch = buf; } if ( pszLineHeader ) { pch += sprintf( pch, "\r\n%s", pszLineHeader ); } else { pch += sprintf( pch, "\r\n\t%3d> ", i ); } lineCount++; //if ( i >= 128 && dlen > 256 ) //{ // PrintRoutine( pContext, "skipping remaining bytes ...\r\n" )); //} } pbyte = &pchData[i]; if ( PrintSize == sizeof(QWORD) && POINTER_IS_ALIGNED( pbyte, ALIGN_QUAD ) && pbyte + sizeof(QWORD) <= pend ) { pch += sprintf( pch, "%I64x ", *(PQWORD)pbyte ); i += sizeof(QWORD); } else if ( PrintSize == sizeof(DWORD) && POINTER_IS_ALIGNED( pbyte, ALIGN_DWORD ) && pbyte + sizeof(DWORD) <= pend ) { pch += sprintf( pch, "%08x ", *(PDWORD)pbyte ); i += sizeof(DWORD); } else if ( PrintSize == sizeof(WORD) && POINTER_IS_ALIGNED( pbyte, ALIGN_WORD ) && pbyte + sizeof(WORD) <= pend ) { pch += sprintf( pch, "%04x ", *(PWORD)pbyte ); i += sizeof(WORD); } else // default to byte print { pch += sprintf( pch, "%02x ", *pbyte ); i++; } } // print remaining bytes in buffer PrintRoutine( pContext, "%s\r\n", buf ); DnsPrint_Unlock(); } VOID DnsPrint_RawOctets( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PSTR pszLineHeader, IN PCHAR pchData, IN DWORD dwLength ) /*++ Routine Description: Print raw octect data. Arguments: pszHeader - Header message/name for RR. pszLineHeader - Header on each line. pchData - data to print dwLength - length of data to print Return Value: None. --*/ { INT i; INT lineCount = 0; CHAR buf[ 2000 ]; PCHAR pch = buf; DnsPrint_Lock(); if ( pszHeader ) { PrintRoutine( pContext, "%s", pszHeader ); } buf[0] = 0; // buffer every 20 lines for speed for ( i = 0; i < (INT)dwLength; i++ ) { if ( !(i%16) ) { if ( lineCount > 10 ) { PrintRoutine( pContext, buf ); lineCount = 0; pch = buf; } if ( pszLineHeader ) { pch += sprintf( pch, "\r\n%s", pszLineHeader ); } else { pch += sprintf( pch, "\r\n%3d> ", i ); } lineCount++; //if ( i >= 128 && dlen > 256 ) //{ // PrintRoutine( pContext, "skipping remaining bytes ...\r\n" )); //} } pch += sprintf( pch, "%02x ", (UCHAR)pchData[i] ); } // print remaining bytes in buffer PrintRoutine( pContext, "%s\r\n", buf ); DnsPrint_Unlock(); } VOID DnsPrint_ParsedRecord( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_PARSED_RR pParsedRR ) /*++ Routine Description: Print parsed RR structure. Arguments: pszHeader - Header message/name for RR. pParsedRR - parsed RR to print Return Value: None. --*/ { if ( !pszHeader ) { pszHeader = "Parsed RR:"; } if ( !pParsedRR ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL ParsedRR ptr." ); return; } PrintRoutine( pContext, "%s\r\n" "\tpchName = %p\r\n" "\tpchRR = %p\r\n" "\tpchData = %p\r\n" "\tpchNextRR = %p\r\n" "\twType = %d\r\n" "\twClass = %d\r\n" "\tTTL = %d\r\n" "\twDataLength = %d\r\n", pszHeader, pParsedRR->pchName, pParsedRR->pchRR, pParsedRR->pchData, pParsedRR->pchNextRR, pParsedRR->Type, pParsedRR->Class, pParsedRR->Ttl, pParsedRR->DataLength ); } // // Winsock RnR structures // VOID DnsPrint_FdSet( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN struct fd_set * pfd_set ) /*++ Routine Description: Print sockets in FD_SET. --*/ { INT count; INT i; DNS_ASSERT( pfd_set ); count = (INT) pfd_set->fd_count; DnsPrint_Lock(); PrintRoutine( pContext, "%s (count = %d)\r\n", pszHeader ? pszHeader : "FD_SET:", count ); for (i=0; ifd_array[i] ); } DnsPrint_Unlock(); } VOID DnsPrint_Sockaddr( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN DWORD Indent, IN PSOCKADDR pSockaddr, IN INT iSockaddrLength ) /*++ Routine Description: Print sockaddr structure and length used in call. --*/ { PSTR pindent = INDENT_STRING( Indent ); if ( !pszHeader ) { pszHeader = "Sockaddr:"; } if ( !pSockaddr ) { PrintRoutine( pContext, "%s%s\tNULL Sockaddr passed to print.\r\n", pindent, pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pContext, "%s%s\r\n" "%s\tpointer = %p\r\n" "%s\tlength = %d\r\n" "%s\tsa_family = %d\r\n", pindent, pszHeader, pindent, pSockaddr, pindent, iSockaddrLength, pindent, pSockaddr->sa_family ); switch ( pSockaddr->sa_family ) { case AF_INET: { PSOCKADDR_IN psin = (PSOCKADDR_IN) pSockaddr; PrintRoutine( pContext, "%s\tsin_port = %04x\r\n" "%s\tsin_addr = %s (%08x)\r\n" "%s\tsin_zero = %08x %08x\r\n", pindent, psin->sin_port, pindent, inet_ntoa( psin->sin_addr ), psin->sin_addr.s_addr, pindent, *(PDWORD) &psin->sin_zero[0], *(PDWORD) &psin->sin_zero[4] ); break; } case AF_INET6: { PSOCKADDR_IN6 psin = (PSOCKADDR_IN6) pSockaddr; CHAR buffer[ IP6_ADDRESS_STRING_BUFFER_LENGTH ]; Dns_Ip6AddressToString_A( buffer, (PIP6_ADDRESS) &psin->sin6_addr ); PrintRoutine( pContext, "%s\tsin6_port = %04x\r\n" "%s\tsin6_flowinfo = %08x\r\n" "%s\tsin6_addr = %s\r\n" "%s\tsin6_scope_id = %08x\r\n", pindent, psin->sin6_port, pindent, psin->sin6_flowinfo, pindent, buffer, pindent, psin->sin6_scope_id ); break; } default: // print unknown in WORDs // limit print as this is probably a busted sockaddr due to bug { DnsPrint_RawBinary( PrintRoutine, pContext, "\tdata: ", pindent, // line header pSockaddr->sa_data, iSockaddrLength < 100 ? iSockaddrLength : 100, sizeof(WORD) ); break; } } DnsPrint_Unlock(); } VOID DnsPrint_AddrInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN DWORD Indent, IN PADDRINFO pAddrInfo ) /*++ Routine Description: Print ADDRINFO structure. --*/ { PSTR pindent = INDENT_STRING( Indent ); if ( !pszHeader ) { pszHeader = "AddrInfo:"; } if ( !pAddrInfo ) { PrintRoutine( pContext, "%s%s NULL AddrInfo.\r\n", pindent, pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pContext, "%s%s\n" "%s\tPtr = %p\r\n" "%s\tNext Ptr = %p\r\n" "%s\tFlags = %08x\r\n" "%s\tFamily = %d\r\n" "%s\tSockType = %d\r\n" "%s\tProtocol = %d\r\n" "%s\tAddrLength = %d\r\n" "%s\tName = %s\r\n", pindent, pszHeader, pindent, pAddrInfo, pindent, pAddrInfo->ai_next, pindent, pAddrInfo->ai_flags, pindent, pAddrInfo->ai_family, pindent, pAddrInfo->ai_socktype, pindent, pAddrInfo->ai_protocol, pindent, pAddrInfo->ai_addrlen, pindent, pAddrInfo->ai_canonname ); DnsPrint_Sockaddr( PrintRoutine, pContext, NULL, Indent + 1, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen ); DnsPrint_Unlock(); } VOID DnsPrint_AddrInfoList( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN DWORD Indent, IN PADDRINFO pAddrInfo ) /*++ Routine Description: Print ADDRINFO structure. --*/ { PADDRINFO paddr = pAddrInfo; PSTR pindent = INDENT_STRING( Indent ); // // list header // if ( !pszHeader ) { pszHeader = "AddrInfo List:"; } if ( !paddr ) { PrintRoutine( pContext, "%s%s NULL AddrInfo List.\r\n", pindent, pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pContext, "%s%s\n", pindent, pszHeader ); // // print each ADDRINFO in list // while ( paddr ) { DnsPrint_AddrInfo( PrintRoutine, pContext, NULL, Indent, paddr ); paddr = paddr->ai_next; } PrintRoutine( pContext, "End of AddrInfo list\n\n" ); DnsPrint_Unlock(); } VOID DnsPrint_SocketAddress( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN DWORD Indent, IN PSOCKET_ADDRESS pSocketAddress ) /*++ Routine Description: Print SOCKET_ADDRESS structure. --*/ { PSTR pindent = INDENT_STRING( Indent ); if ( !pszHeader ) { pszHeader = "SocketAddress:"; } if ( !pSocketAddress ) { PrintRoutine( pContext, "%s%s NULL SocketAddress.\r\n", pindent, pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pContext, "%s%s\n" "%s\tpSockaddr = %p\r\n" "%s\tiSockaddrLength = %d\r\n", pindent, pszHeader, pindent, pSocketAddress->lpSockaddr, pindent, pSocketAddress->iSockaddrLength ); DnsPrint_Sockaddr( PrintRoutine, pContext, NULL, Indent, pSocketAddress->lpSockaddr, pSocketAddress->iSockaddrLength ); DnsPrint_Unlock(); } VOID DnsPrint_CsAddr( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN DWORD Indent, IN PCSADDR_INFO pCsAddr ) /*++ Routine Description: Print CSADDR_INFO structure. Arguments: PrintRoutine - routine to print with pParam - ptr to print context pszHeader - header Indent - indent count, for formatting CSADDR inside larger struct pCsAddr - ptr to CSADDRINFO to print Return Value: None. --*/ { PSTR pindent = INDENT_STRING( Indent ); if ( !pszHeader ) { pszHeader = "CSAddrInfo:"; } if ( !pCsAddr ) { PrintRoutine( pContext, "%s%s \tNULL CSADDR_INFO ptr.\r\n", pindent, pszHeader ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s%s\r\n" "%s\tPtr = %p\n" "%s\tSocketType = %d\n" "%s\tProtocol = %d\n", pindent, pszHeader, pindent, pCsAddr, pindent, pCsAddr->iSocketType, pindent, pCsAddr->iProtocol ); DnsPrint_SocketAddress( PrintRoutine, pContext, "LocalAddress:", Indent, & pCsAddr->LocalAddr ); DnsPrint_SocketAddress( PrintRoutine, pContext, "RemoteAddress:", Indent, & pCsAddr->RemoteAddr ); DnsPrint_Unlock(); } VOID DnsPrint_AfProtocolsArray( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PAFPROTOCOLS pProtocolArray, IN DWORD ProtocolCount ) /*++ Routine Description: Print AFPROTOCOLS array. Arguments: PrintRoutine - routine to print with pszHeader - header pProtocolArray - protocols array ProtocolCount - array count Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "AFPROTOCOLS Array:"; } // print // - array + count // - each protocol element DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" "\tProtocol Array = %p\r\n" "\tProtocol Count = %d\r\n", pszHeader, pProtocolArray, ProtocolCount ); if ( pProtocolArray ) { for ( i=0; ilpServiceClassId ); DnsStringPrint_Guid( nameSpaceGuidBuffer, pQuerySet->lpNSProviderId ); // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" "\tSize = %d\r\n" "\tServiceInstanceName = %S%s\r\n" "\tService GUID = (%p) %s\r\n" "\tWSA version = %p %x %d\r\n" "\tComment = %S%s\r\n" "\tName Space = %d %s\r\n" "\tName Space GUID = (%p) %s\r\n" "\tContext = %S%s\r\n" "\tNumberOfProtocols = %d\r\n" "\tProtocol Array = %p\r\n" "\tQueryString = %S%s\r\n" "\tCS Addr Count = %d\r\n" "\tCS Addr Array = %p\r\n" "\tOutput Flags = %08x\r\n" "\tpBlob = %p\r\n", pszHeader, pQuerySet->dwSize, DNSSTRING_WIDE( fUnicode, pQuerySet->lpszServiceInstanceName ), DNSSTRING_ANSI( fUnicode, pQuerySet->lpszServiceInstanceName ), pQuerySet->lpServiceClassId, serviceGuidBuffer, pQuerySet->lpVersion, ( pQuerySet->lpVersion ) ? pQuerySet->lpVersion->dwVersion : 0, ( pQuerySet->lpVersion ) ? pQuerySet->lpVersion->ecHow : 0, DNSSTRING_WIDE( fUnicode, pQuerySet->lpszComment ), DNSSTRING_ANSI( fUnicode, pQuerySet->lpszComment ), pQuerySet->dwNameSpace, Dns_GetRnrNameSpaceIdString( pQuerySet->dwNameSpace ), pQuerySet->lpNSProviderId, nameSpaceGuidBuffer, DNSSTRING_WIDE( fUnicode, pQuerySet->lpszContext ), DNSSTRING_ANSI( fUnicode, pQuerySet->lpszContext ), pQuerySet->dwNumberOfProtocols, pQuerySet->lpafpProtocols, DNSSTRING_WIDE( fUnicode, pQuerySet->lpszQueryString ), DNSSTRING_ANSI( fUnicode, pQuerySet->lpszQueryString ), pQuerySet->dwNumberOfCsAddrs, pQuerySet->lpcsaBuffer, pQuerySet->dwOutputFlags, pQuerySet->lpBlob ); // print address-family\protocols array if ( pQuerySet->lpafpProtocols ) { DnsPrint_AfProtocolsArray( PrintRoutine, pContext, "\tAFPROTOCOLS Array:", pQuerySet->lpafpProtocols, pQuerySet->dwNumberOfProtocols ); } // print CSADDR_INFO array if ( pQuerySet->dwNumberOfCsAddrs && pQuerySet->lpcsaBuffer ) { PrintRoutine( pContext, "--- CS_ADDR array:\r\n" ); for ( i=0; idwNumberOfCsAddrs; i++ ) { DnsPrint_CsAddr( PrintRoutine, pContext, NULL, 1, // indent one level & pQuerySet->lpcsaBuffer[i] ); } } // print blob (the hostent) // // DCR_FIX0: need some sort of test for blob type? // - most blobs are hostent, but some are servent // if ( pQuerySet->lpBlob ) { GUID ianaGuid = SVCID_INET_SERVICEBYNAME; PrintRoutine( pContext, "--- BLOB:\n" "\tcbSize = %d\r\n" "\tpBlobData = %p\r\n", pQuerySet->lpBlob->cbSize, pQuerySet->lpBlob->pBlobData ); // note: can't print blob as hostent // 1) may not be hostent // 2) is passed with offsets rather than pointers DnsPrint_RawBinary( PrintRoutine, pContext, NULL, "\t\t", pQuerySet->lpBlob->pBlobData, pQuerySet->lpBlob->cbSize, sizeof(DWORD) ); } DnsPrint_Unlock(); } VOID DnsPrint_WsaNsClassInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PWSANSCLASSINFO pInfo, IN BOOL fUnicode ) /*++ Routine Description: Print WSACLASSINFO structure. Arguments: PrintRoutine - routine to print with pszHeader - header pInfo - ptr to WSACLASSINFO to print fUnicode - TRUE if WSACLASSINFO is wide (WSACLASSINFOW) FALSE if ANSI Return Value: None. --*/ { if ( !pszHeader ) { pszHeader = "WSANsClassInfo:"; } if ( !pInfo ) { PrintRoutine( pContext, "%s NULL NsClassInfo ptr\r\n", pszHeader ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" "\tPtr = %d\r\n" "\tName = %S%s\r\n" "\tName Space = %d\r\n" "\tValue Type = %d\r\n" "\tValue Size = %d\r\n" "\tpValue = %p\r\n", pszHeader, pInfo, DNSSTRING_WIDE( fUnicode, pInfo->lpszName ), DNSSTRING_ANSI( fUnicode, pInfo->lpszName ), pInfo->dwNameSpace, pInfo->dwValueType, pInfo->dwValueSize, pInfo->lpValue ); if ( pInfo->lpValue ) { PrintRoutine( pContext, "--- Value:\r\n" ); DnsPrint_RawBinary( PrintRoutine, pContext, NULL, "\t\t", pInfo->lpValue, pInfo->dwValueSize, sizeof(BYTE) // print in bytes ); } DnsPrint_Unlock(); } VOID DnsPrint_WsaServiceClassInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN LPWSASERVICECLASSINFO pInfo, IN BOOL fUnicode ) /*++ Routine Description: Print WSASERVICECLASSINFO structure. Arguments: PrintRoutine - routine to print with pszHeader - header pInfo - ptr to WSASERVICECLASSINFO to print fUnicode - TRUE if WSASERVICECLASSINFO is wide (WSASERVICECLASSINFOW) FALSE if ANSI Return Value: None. --*/ { CHAR serviceClassGuidBuffer[ GUID_STRING_BUFFER_LENGTH ]; if ( !pszHeader ) { pszHeader = "WSAServiceClassInfo:"; } if ( !pInfo ) { PrintRoutine( pContext, "%s NULL ServiceClassInfo ptr\r\n", pszHeader ); return; } // convert GUID to strings DnsStringPrint_Guid( serviceClassGuidBuffer, pInfo->lpServiceClassId ); // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" "\tPtr = %p\r\n" "\tClass GUID = (%p) %s\r\n" "\tClassName = %S%s\r\n" "\tClass Info Count = %d\r\n" "\tClass Info Array = %p\r\n", pszHeader, pInfo, serviceClassGuidBuffer, DNSSTRING_WIDE( fUnicode, pInfo->lpszServiceClassName ), DNSSTRING_ANSI( fUnicode, pInfo->lpszServiceClassName ), pInfo->dwCount, pInfo->lpClassInfos ); if ( pInfo->lpClassInfos ) { DWORD i; for ( i=0; idwCount; i++ ) { DnsPrint_WsaNsClassInfo( PrintRoutine, pContext, NULL, // default header & pInfo->lpClassInfos[i], fUnicode ); } } DnsPrint_Unlock(); } VOID DnsPrint_Hostent( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PHOSTENT pHostent, IN BOOL fUnicode ) /*++ Routine Description: Print hostent structure. Arguments: PrintRoutine - routine to print with pszHeader - header pHostent - ptr to hostent fUnicode - TRUE if hostent is unicode FALSE if ANSI Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Hostent:"; } if ( !pHostent ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL Hostent ptr." ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" "\th_name = %p %S%s\n" "\th_aliases = %p\n" "\th_addrtype = %d\n" "\th_length = %d\n" "\th_addrlist = %p\n", pszHeader, pHostent->h_name, DNSSTRING_WIDE( fUnicode, pHostent->h_name ), DNSSTRING_ANSI( fUnicode, pHostent->h_name ), pHostent->h_aliases, pHostent->h_addrtype, pHostent->h_length, pHostent->h_addr_list ); // print the aliases if ( pHostent->h_aliases ) { PSTR * paliasArray = pHostent->h_aliases; PSTR palias; while ( palias = *paliasArray++ ) { PrintRoutine( pContext, "\tAlias = (%p) %S%s\n", palias, DNSSTRING_WIDE( fUnicode, palias ), DNSSTRING_ANSI( fUnicode, palias ) ); } } // print the addresses if ( pHostent->h_addr_list ) { PCHAR * ppaddr = pHostent->h_addr_list; PCHAR pip; INT i = 0; INT family = pHostent->h_addrtype; INT addrLength = pHostent->h_length; CHAR stringBuf[ IP6_ADDRESS_STRING_BUFFER_LENGTH ]; while ( pip = ppaddr[i] ) { DWORD bufLength = IP6_ADDRESS_STRING_BUFFER_LENGTH; Dns_AddressToString_A( stringBuf, & bufLength, pip, addrLength, family ); PrintRoutine( pContext, "\tAddr[%d] = %s \t(ptr=%p)\n", i, stringBuf, pip ); i++; } } DnsPrint_Unlock(); } // // Query print routines // VOID DnsPrint_QueryBlob( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PQUERY_BLOB pBlob ) /*++ Routine Description: Print query blob. Arguments: PrintRoutine - routine to print with pContext - print context pszHeader - header pBlob - query info Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Query Blob:"; } if ( !pBlob ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL Query Blob ptr." ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "\tname orig %S\n" "\tname orig wire %s\n" "\tname wire %s\n" "\ttype %d\n" "\tflags %08x\n" "\tname length %d\n" "\tname attributes %08x\n" "\tquery count %d\n" "\tname flags %08x\n" "\tfappendedName %d\n" "\tstatus %d\n" "\trcode %d\n" "\tnetfail status %d\n" "\tcache negative %d\n" "\tno ip local %d\n" "\trecords %p\n" "\tlocal records %p\n" "\tnetwork info %p\n" "\tserver IPs %p\n" "\tpmsg %p\n" "\tevent %p\n", pBlob->pNameOrig, pBlob->pNameOrigWire, pBlob->pNameWire, pBlob->wType, pBlob->Flags, pBlob->NameLength, pBlob->NameAttributes, pBlob->QueryCount, pBlob->NameFlags, pBlob->fAppendedName, pBlob->Status, pBlob->Rcode, pBlob->NetFailureStatus, pBlob->fCacheNegative, pBlob->fNoIpLocal, pBlob->pRecords, pBlob->pLocalRecords, pBlob->pNetworkInfo, pBlob->pDnsServers, pBlob->pRecvMsg, pBlob->hEvent ); // DCR_FIX0: cleanup when results in use DnsPrint_RecordSet( PrintRoutine, pContext, "Records:\n", pBlob->pRecords ); // DCR_FIX0: use results when ready DnsPrint_Unlock(); } VOID DnsPrint_QueryResults( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_RESULTS pResults ) /*++ Routine Description: Print query results. Arguments: PrintRoutine - routine to print with pContext - print context pszHeader - header pResults - results info Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Results:"; } if ( !pResults ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL Results ptr." ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "\tstatus %d\n" "\trcode %d\n" "\tserver %s\n" "\tpanswer %p\n" "\tpalias %p\n" "\tpauthority %p\n" "\tpadditional %p\n" "\tpsig %p\n" "\tpmsg %p\n", pResults->Status, pResults->Rcode, IP_STRING( pResults->ServerIp ), pResults->pAnswerRecords, pResults->pAliasRecords, pResults->pAuthorityRecords, pResults->pAdditionalRecords, pResults->pSigRecords, pResults->pMessage ); DnsPrint_RecordSet( PrintRoutine, pContext, "\tAnswer records:\n", pResults->pAnswerRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "\tAlias records:\n", pResults->pAliasRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "\tAuthority records:\n", pResults->pAuthorityRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "\tAdditional records:\n", pResults->pAdditionalRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "\tSignature records:\n", pResults->pSigRecords ); DnsPrint_Unlock(); } VOID DnsPrint_ParsedMessage( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_PARSED_MESSAGE pParsed ) /*++ Routine Description: Print parsed message. Arguments: PrintRoutine - routine to print with pContext - print context pszHeader - header pResults - query info Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Parsed Message:"; } if ( !pParsed ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL Parsed Message ptr." ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "\tstatus %d (%08x)\n" "\tchar set %d\n", pParsed->Status, pParsed->Status, pParsed->CharSet ); PrintRoutine( pContext, "\tquestion:\n" "\t\tname %S%s\n" "\t\ttype %d\n" "\t\tclass %d\n", PRINT_STRING_WIDE_CHARSET( pParsed->pQuestionName, pParsed->CharSet ), PRINT_STRING_ANSI_CHARSET( pParsed->pQuestionName, pParsed->CharSet ), pParsed->QuestionType, pParsed->QuestionClass ); DnsPrint_RecordSet( PrintRoutine, pContext, "Answer records:\n", pParsed->pAnswerRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Alias records:\n", pParsed->pAliasRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Authority records:\n", pParsed->pAuthorityRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Additional records:\n", pParsed->pAdditionalRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Signature records:\n", pParsed->pSigRecords ); DnsPrint_Unlock(); } VOID DnsPrint_QueryInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PDNS_QUERY_INFO pQueryInfo ) /*++ Routine Description: Print query info Arguments: PrintRoutine - routine to print with pContext - print context pszHeader - header pQueryInfo - query info Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Query Info:"; } if ( !pQueryInfo ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL Query Info ptr." ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s\n" "\tpointer %p\n" "\tstatus %d (%08x)\n" "\tchar set %d\n" "\tname %S%s\n" "\tname resv. %s\n" "\ttype %d\n" "\trcode %d\n" "\tflags %08x\n" "\tpanswer %p\n" "\tpalias %p\n" "\tpauthority %p\n" "\tpadditional %p\n" //"\tpsig %p\n" "\tevent %p\n" "\tpDnsServers %p\n" "\tpmsg %p\n", pszHeader, pQueryInfo, pQueryInfo->Status, pQueryInfo->Status, pQueryInfo->CharSet, PRINT_STRING_WIDE_CHARSET( pQueryInfo->pName, pQueryInfo->CharSet ), PRINT_STRING_ANSI_CHARSET( pQueryInfo->pName, pQueryInfo->CharSet ), pQueryInfo->pReservedName, pQueryInfo->Type, pQueryInfo->Rcode, pQueryInfo->Flags, pQueryInfo->pAnswerRecords, pQueryInfo->pAliasRecords, pQueryInfo->pAuthorityRecords, pQueryInfo->pAdditionalRecords, //pQueryInfo->pSigRecords, pQueryInfo->hEvent, pQueryInfo->pDnsServers, pQueryInfo->pMessage ); DnsPrint_RecordSet( PrintRoutine, pContext, "Answer records:\n", pQueryInfo->pAnswerRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Alias records:\n", pQueryInfo->pAliasRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Authority records:\n", pQueryInfo->pAuthorityRecords ); DnsPrint_RecordSet( PrintRoutine, pContext, "Additional records:\n", pQueryInfo->pAdditionalRecords ); //DnsPrint_RecordSet( // "Signature records:\n", // pQueryInfo->pSigRecords ); DnsPrint_Unlock(); } VOID DnsPrint_EnvarInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PENVAR_DWORD_INFO pEnvar ) /*++ Routine Description: Print envar data Arguments: PrintRoutine - routine to print with pContext - print context pszHeader -- header to print with pEnvar -- ptr to envar info Return Value: ERROR_SUCCESS if successful. ErrorCode on failure. --*/ { PrintRoutine( pContext, "%s\n" "\tId = %d\n" "\tValue = %p\n" "\tfFound = %d\n", pszHeader ? pszHeader : "Envar Info:", pEnvar->Id, pEnvar->Value, pEnvar->fFound ); } // // Network info print routines. // VOID DnsPrint_NetworkInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pPrintContext, IN LPSTR pszHeader, IN PDNS_NETINFO pNetworkInfo ) /*++ Routine Description: Prints and validates network info structure. Should also touch all the memory and AV when bogus. Arguments: pNetworkInfo -- network info to print Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "NetworkInfo:"; } if ( !pNetworkInfo ) { PrintRoutine( pPrintContext, "%s NULL NetworkInfo.\n", pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pPrintContext, "%s\n" "\tpointer = %p\n" "\tpszHostName = %s\n" "\tpszDomainName = %s\n" "\tpSearchList = %p\n" "\tTimeStamp = %d\n" "\tInfoFlags = %08x\n" "\tReturnFlags = %08x\n" "\tAdapterCount = %d\n" "\tAdapterArraySize = %d\n", pszHeader, pNetworkInfo, pNetworkInfo->pszHostName, pNetworkInfo->pszDomainName, pNetworkInfo->pSearchList, pNetworkInfo->TimeStamp, pNetworkInfo->InfoFlags, pNetworkInfo->ReturnFlags, pNetworkInfo->AdapterCount, pNetworkInfo->MaxAdapterCount ); // print search list DnsPrint_SearchList( PrintRoutine, pPrintContext, "Search List: ", pNetworkInfo->pSearchList ); // print server lists for ( i=0; i < pNetworkInfo->AdapterCount; i++ ) { CHAR header[60]; sprintf( header, "AdapterInfo[%d]:", i ); DnsPrint_AdapterInfo( PrintRoutine, pPrintContext, header, pNetworkInfo->AdapterArray[i] ); } PrintRoutine( pPrintContext, "\n" ); DnsPrint_Unlock(); } VOID DnsPrint_AdapterInfo( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pPrintContext, IN LPSTR pszHeader, IN PDNS_ADAPTER pAdapter ) /*++ Routine Description: Prints and validates DNS adapter info. Should also touch all the memory and AV when bogus. Arguments: pAdapter -- DNS adapter to print Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Adapter Info:"; } if ( !pAdapter ) { PrintRoutine( pPrintContext, "%s NULL Adapter info.\n", pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pPrintContext, "%s\n" "\tpointer = %p\n" "\tDomain = %s\n" "\tGuid Name = %s\n" "\tInterfaceIndex = %d\n" "\tInfoFlags = %08x\n" "\tStatus = %d\n" "\tRunFlags = %08x\n" "\tServerIndex = %d\n" "\tServerCount = %d\n" "\tServerArraySize = %d\n", pszHeader, pAdapter, pAdapter->pszAdapterDomain, pAdapter->pszAdapterGuidName, pAdapter->InterfaceIndex, pAdapter->InfoFlags, pAdapter->Status, pAdapter->RunFlags, pAdapter->ServerIndex, pAdapter->ServerCount, pAdapter->MaxServerCount ); // DNS server info for ( i=0; i < pAdapter->ServerCount; i++ ) { // DCR: IP6 DNS server address PrintRoutine( pPrintContext, "\tDNS Server [%d]:\n" "\t\tIpAddr = %s\n" "\t\tPriority = %d\n" "\t\tStatus = %d (%08x)\n", i, IP_STRING( pAdapter->ServerArray[i].IpAddress ), pAdapter->ServerArray[i].Priority, pAdapter->ServerArray[i].Status, pAdapter->ServerArray[i].Status ); } // IP address info DnsPrint_IpArray( PrintRoutine, pPrintContext, "IP Addrs", "IP", pAdapter->pAdapterIPAddresses ); DnsPrint_IpArray( PrintRoutine, pPrintContext, "IP Addr Subnets", "Mask", pAdapter->pAdapterIPSubnetMasks ); DnsPrint_Unlock(); } VOID DnsPrint_SearchList( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pPrintContext, IN LPSTR pszHeader, IN PSEARCH_LIST pSearchList ) /*++ Routine Description: Prints and validates DNS search list. Should also touch all the memory and AV when bogus. Arguments: pSearchList -- search list to print Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "DNS Search List:"; } if ( ! pSearchList ) { PrintRoutine( pPrintContext, "%s NULL search list.\n", pszHeader ); return; } DnsPrint_Lock(); PrintRoutine( pPrintContext, "%s\n" "\tpointer = %p\n" "\tpszDomain = %s\n" "\tcNameCount = %d\n" "\tcTotalListSize = %d\n" "\tCurrentName = %d\n" "\tSearchListNames:\n", pszHeader, pSearchList, pSearchList->pszDomainOrZoneName, pSearchList->NameCount, pSearchList->MaxNameCount, pSearchList->CurrentNameIndex ); for ( i=0; i < pSearchList->NameCount; i++ ) { PrintRoutine( pPrintContext, "\t\t%s (Flags: %08x)\n", pSearchList->SearchNameArray[i].pszName, pSearchList->SearchNameArray[i].Flags ); } DnsPrint_Unlock(); } VOID DnsPrint_HostentBlob( IN PRINT_ROUTINE PrintRoutine, IN OUT PPRINT_CONTEXT pContext, IN PSTR pszHeader, IN PHOSTENT_BLOB pBlob ) /*++ Routine Description: Print hostent structure. Arguments: PrintRoutine - routine to print with pszHeader - header pBlob - ptr to hostent blob Return Value: None. --*/ { DWORD i; if ( !pszHeader ) { pszHeader = "Hostent Blob:"; } if ( !pBlob ) { PrintRoutine( pContext, "%s %s\r\n", pszHeader, "NULL Hostent blob ptr." ); return; } // print the struct DnsPrint_Lock(); PrintRoutine( pContext, "%s\r\n" "\tPtr = %p\n" "\tpHostent = %p\n" "\tfAllocatedBlob = %d\n" "\tfAllocatedBuf = %d\n" "\tpBuffer = %p\n" "\tBufferLength = %d\n" "\tAvailLength = %d\n" "\tpAvailBuffer = %p\n" "\tpCurrent = %p\n" "\tBytesLeft = %d\n" "\tMaxAliasCount = %d\n" "\tAliasCount = %d\n" "\tMaxAddrCount = %d\n" "\tAddrCount = %d\n" "\tfWroteName = %d\n" "\tfUnicode = %d\n" "\tCharSet = %d\n", pszHeader, pBlob, pBlob->pHostent, pBlob->fAllocatedBlob, pBlob->fAllocatedBuf, pBlob->pBuffer, pBlob->BufferLength, pBlob->AvailLength, pBlob->pAvailBuffer, pBlob->pCurrent, pBlob->BytesLeft, pBlob->MaxAliasCount, pBlob->AliasCount, pBlob->MaxAddrCount, pBlob->AddrCount, pBlob->fWroteName, pBlob->fUnicode, pBlob->CharSet ); // print the hostent if ( pBlob->pHostent ) { DnsPrint_Hostent( PrintRoutine, pContext, NULL, pBlob->pHostent, pBlob->fUnicode ); } DnsPrint_Unlock(); } // // End of print.c //