/*++ Copyright (c) 1998, Microsoft Corporation Module Name: dnsmsg.c Abstract: This module contains code for the DNS proxy's message-processing. Author: Abolade Gbadegesin (aboladeg) 9-Mar-1998 Revision History: Raghu Gatta (rgatta) 1-Dec-2000 Rewrite + Cleanup + New Functions --*/ #include "precomp.h" #pragma hdrstop // // EXTERNAL DECLARATIONS // extern "C" DWORD G_UseEdns; VOID DnsProcessQueryMessage( PDNS_INTERFACE Interfacep, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked to process a DNS query message. Arguments: Interfacep - the interface on which the query was received Bufferp - the buffer containing the query Return Value: none. Environment: Invoked internally in the context of a worker-thread completion routine, with an outstanding reference to 'Interfacep' from the time the read-operation was begun. --*/ { PVOID Context; PVOID Context2; ULONG Error; PDNS_QUERY Queryp; PDNS_HEADER Headerp; BOOLEAN Referenced = TRUE; SOCKET Socket; LPVOID lpMsgBuf; PROFILE("DnsProcessQueryMessage"); #if DBG NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: dumping %d bytes", Bufferp->BytesTransferred ); NhDump( TRACE_FLAG_DNS, Bufferp->Buffer, Bufferp->BytesTransferred, 1 ); #endif InterlockedIncrement( reinterpret_cast(&DnsStatistics.QueriesReceived) ); Headerp = (PDNS_HEADER)Bufferp->Buffer; Socket = Bufferp->Socket; Context = Bufferp->Context; Context2 = Bufferp->Context2; // // if the Broadcast bit (9) was set, we leave it as is // instead of zeroing it // //if (Headerp->Broadcast) { // Headerp->Broadcast = 0; //} if (Headerp->Opcode == DNS_OPCODE_QUERY || Headerp->Opcode == DNS_OPCODE_IQUERY || Headerp->Opcode == DNS_OPCODE_SERVER_STATUS) { // // Query the local DNS Resolver cache before proxying // // // Unpack // DNS_STATUS dnsStatus; DNS_PARSED_MESSAGE dnsParsedMsg; PDNS_MESSAGE_BUFFER pDnsBuffer = NULL; PDNS_MSG_BUF pDnsMsgBuf = NULL; PDNS_RECORD pQueryResultsSet = NULL; WORD wMessageLength; DWORD dwFlags, dwQueryOptions; DNS_CHARSET CharSet; BOOL fQ4DefaultSuffix = FALSE; ZeroMemory(&dnsParsedMsg, sizeof(DNS_PARSED_MESSAGE)); pDnsBuffer = (PDNS_MESSAGE_BUFFER) Headerp; wMessageLength = (WORD) Bufferp->BytesTransferred; dwFlags = DNS_PARSE_FLAG_ONLY_QUESTION; CharSet = DnsCharSetUtf8; // // Dns* functions require byte flipping // DNS_BYTE_FLIP_HEADER_COUNTS(&pDnsBuffer->MessageHead); dnsStatus = Dns_ParseMessage( &dnsParsedMsg, pDnsBuffer, wMessageLength, dwFlags, CharSet ); if (NO_ERROR == dnsStatus) { NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: Dns_ParseMessage succeeded!!" ); // // Make a note of whether this question was for our private // default domain (ie mshome.net) // { // // the question name is in UTF_8 form // PWCHAR pszQName = NULL; DWORD dwUtf8Size = 0, dwSize; dwUtf8Size = strlen((char *)dnsParsedMsg.pQuestionName); dwSize = DnsGetBufferLengthForStringCopy( (char *)dnsParsedMsg.pQuestionName, dwUtf8Size, DnsCharSetUtf8, DnsCharSetUnicode ); if (!dwSize) { // // invalid input string // DWORD dwRet = GetLastError(); lpMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRet, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: DnsGetBufferLengthForStringCopy" " returned (0x%08x) %S", dwRet, lpMsgBuf ); if (lpMsgBuf) LocalFree(lpMsgBuf); } else { pszQName = reinterpret_cast(NH_ALLOCATE(dwSize)); if (!pszQName) { NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: allocation " "failed for pszQName" ); } else { ZeroMemory(pszQName, dwSize); DnsUtf8ToUnicode( (char *)dnsParsedMsg.pQuestionName, dwUtf8Size, pszQName, dwSize ); fQ4DefaultSuffix = IsSuffixValid( pszQName, DNS_HOMENET_SUFFIX ); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: %S (%s)", pszQName, (fQ4DefaultSuffix?"TRUE":"FALSE") ); NH_FREE(pszQName); } } } // // Query // dwQueryOptions = ( DNS_QUERY_STANDARD | DNS_QUERY_CACHE_ONLY | DNS_QUERY_TREAT_AS_FQDN | //DNS_QUERY_ALLOW_EMPTY_AUTH_RESP | 0 ); dnsStatus = DnsQuery_UTF8( (LPSTR) dnsParsedMsg.pQuestionName, dnsParsedMsg.QuestionType, dwQueryOptions, NULL, &pQueryResultsSet, NULL ); } if (dnsStatus) { lpMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dnsStatus, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: (0x%08x) %S", dnsStatus, lpMsgBuf ); if (lpMsgBuf) LocalFree(lpMsgBuf); } if ((NO_ERROR == dnsStatus) && (pQueryResultsSet) // ??? what do i check to see if // there was actually something useful // returned from the cache ) { NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: results found in the local DNS Resolver Cache" ); // // Pack & Send back answer; return // // set response bit dnsParsedMsg.Header.IsResponse = 1; // set the section field of every DNS_RECORD given to us // *** NEED TO CHANGE THIS LATER *** PDNS_RECORD pRR = pQueryResultsSet; DWORD cnt = 0; while (pRR) { pRR->Flags.S.Section = 1; cnt++; pRR = pRR->pNext; } NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: %d records", cnt ); // set global EDNS OPT field to 0 every time // *** NEED TO CHANGE THIS LATER *** //G_UseEdns = 0; pDnsMsgBuf = Dns_BuildPacket( &dnsParsedMsg.Header, // ??? parsed message header should be OK TRUE, // ??? no header count copy - counts done automatically? dnsParsedMsg.pQuestionName, dnsParsedMsg.QuestionType, pQueryResultsSet, dwQueryOptions, TRUE // set to update because of G_UseEdns workaround ); if (NULL == pDnsMsgBuf) { lpMsgBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: Dns_BuildPacket failed (%S)", lpMsgBuf ); if (lpMsgBuf) LocalFree(lpMsgBuf); } else { DWORD dwDnsPktSize = (DWORD)(sizeof(DNS_HEADER) + ((PCHAR)pDnsMsgBuf->pCurrent - (PCHAR)pDnsMsgBuf->MessageBody)); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: Dns_BuildPacket returned pkt of size %d (%d) bytes", dwDnsPktSize, DNS_MESSAGE_OFFSET(pDnsMsgBuf, pDnsMsgBuf->pCurrent) ); // // send back the answer retrieved from the cache // PNH_BUFFER NewBufferp = NhAcquireVariableLengthBuffer( dwDnsPktSize ); if (!NewBufferp) { NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: could not acquire buffer" ); } else { // // Dns* functions return in host order ??? // DNS_BYTE_FLIP_HEADER_COUNTS(&pDnsMsgBuf->MessageHead); // // Reference the interface now since we are replying // to the query // EnterCriticalSection(&DnsInterfaceLock); if (!DNS_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&DnsInterfaceLock); Referenced = FALSE; } else { LeaveCriticalSection(&DnsInterfaceLock); ACQUIRE_LOCK(Interfacep); memcpy( NewBufferp->Buffer, &pDnsMsgBuf->MessageHead, dwDnsPktSize ); Error = NhWriteDatagramSocket( &DnsComponentReference, Bufferp->Socket, Bufferp->ReadAddress.sin_addr.s_addr, Bufferp->ReadAddress.sin_port, NewBufferp, dwDnsPktSize, DnsWriteCompletionRoutine, Interfacep, NULL ); RELEASE_LOCK(Interfacep); if (!Error) { InterlockedIncrement( reinterpret_cast(&DnsStatistics.ResponsesSent) ); } else { NhReleaseBuffer(NewBufferp); DNS_DEREFERENCE_INTERFACE(Interfacep); NhWarningLog( IP_DNS_PROXY_LOG_RESPONSE_FAILED, Error, "%I", NhQueryAddressSocket(Socket) ); } } } LocalFree(pDnsMsgBuf); } // // CLEANUP // DnsRecordListFree( pQueryResultsSet, DnsFreeRecordListDeep ); // buffer is reposted below } else if (//(DNS_ERROR_RECORD_DOES_NOT_EXIST == dnsStatus) && (fQ4DefaultSuffix)) { // // this was a question for our default suffix // we send a name error reply back to the client // - note however that we dont publish an SOA // record for our default suffix domain // // // Undoing Flip above // DNS_BYTE_FLIP_HEADER_COUNTS(&pDnsBuffer->MessageHead); DWORD dwDnsPktSize = Bufferp->BytesTransferred; NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: returning error message" ); // // send back the negative answer // PNH_BUFFER NewBufferp = NhAcquireVariableLengthBuffer( dwDnsPktSize ); if (!NewBufferp) { NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: could not acquire buffer" ); } else { // // Reference the interface now since we are replying // to the query // EnterCriticalSection(&DnsInterfaceLock); if (!DNS_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&DnsInterfaceLock); Referenced = FALSE; } else { LeaveCriticalSection(&DnsInterfaceLock); ACQUIRE_LOCK(Interfacep); memcpy( NewBufferp->Buffer, Bufferp->Buffer, dwDnsPktSize ); PDNS_HEADER NewHeaderp = (PDNS_HEADER)NewBufferp->Buffer; // // set response bit // NewHeaderp->IsResponse = 1; // // set "Name does not exist" error in the RCode field // NewHeaderp->ResponseCode = DNS_RCODE_NXDOMAIN; Error = NhWriteDatagramSocket( &DnsComponentReference, Bufferp->Socket, Bufferp->ReadAddress.sin_addr.s_addr, Bufferp->ReadAddress.sin_port, NewBufferp, dwDnsPktSize, DnsWriteCompletionRoutine, Interfacep, NULL ); RELEASE_LOCK(Interfacep); if (!Error) { InterlockedIncrement( reinterpret_cast(&DnsStatistics.ResponsesSent) ); } else { NhReleaseBuffer(NewBufferp); DNS_DEREFERENCE_INTERFACE(Interfacep); NhWarningLog( IP_DNS_PROXY_LOG_RESPONSE_FAILED, Error, "%I", NhQueryAddressSocket(Socket) ); } } } // buffer is reposted below } else { // // Undoing Flip above // DNS_BYTE_FLIP_HEADER_COUNTS(&pDnsBuffer->MessageHead); // // Reference the interface now in case we need to forward the query // EnterCriticalSection(&DnsInterfaceLock); if (DNS_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&DnsInterfaceLock); } else { LeaveCriticalSection(&DnsInterfaceLock); Referenced = FALSE; } ACQUIRE_LOCK(Interfacep); // // See if this query is already pending; // if not, create a record for it on the receiving interface. // if (DnsIsPendingQuery(Interfacep, Bufferp)) { RELEASE_LOCK(Interfacep); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: query already pending" ); if (Referenced) { DNS_DEREFERENCE_INTERFACE(Interfacep); } } else if (!Referenced || !(Queryp = DnsRecordQuery(Interfacep, Bufferp))) { RELEASE_LOCK(Interfacep); NhTrace( TRACE_FLAG_DNS, "DnsProcessQueryMessage: query could not be created" ); if (Referenced) { DNS_DEREFERENCE_INTERFACE(Interfacep); } } else { // // Write the new ID in the query // Headerp->Xid = Queryp->QueryId; // // Send the query to our servers // Error = DnsSendQuery( Interfacep, Queryp, FALSE ); // // This buffer is now associated with an outstanding query, // so don't repost it below. // if (!Error) { Bufferp = NULL; RELEASE_LOCK(Interfacep); } else { // // Delete the query, but not the buffer, which we repost below // Queryp->Bufferp = NULL; DnsDeleteQuery(Interfacep, Queryp); RELEASE_LOCK(Interfacep); DNS_DEREFERENCE_INTERFACE(Interfacep); } } } // // cleanup question name // LocalFree(dnsParsedMsg.pQuestionName); // ??? is LocalFree OK? } // // Post another read // EnterCriticalSection(&DnsInterfaceLock); if (!DNS_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&DnsInterfaceLock); } else { LeaveCriticalSection(&DnsInterfaceLock); do { Error = NhReadDatagramSocket( &DnsComponentReference, Socket, Bufferp, DnsReadCompletionRoutine, Context, Context2 ); // // A connection-reset error indicates that our last *send* // could not be delivered at its destination. // We could hardly care less; so issue the read again, // immediately. // } while (Error == WSAECONNRESET); if (Error) { ACQUIRE_LOCK(Interfacep); DnsDeferReadInterface(Interfacep, Socket); RELEASE_LOCK(Interfacep); DNS_DEREFERENCE_INTERFACE(Interfacep); NhErrorLog( IP_DNS_PROXY_LOG_RECEIVE_FAILED, Error, "%I", NhQueryAddressSocket(Socket) ); if (Bufferp) { NhReleaseBuffer(Bufferp); } } } } // DnsProcessQueryMessage VOID DnsProcessResponseMessage( PDNS_INTERFACE Interfacep, PNH_BUFFER Bufferp ) /*++ Routine Description: This routine is invoked to process a DNS response message. Arguments: Interfacep - the interface on which the query was received Bufferp - the buffer containing the query Return Value: none. Environment: Invoked internally in the context of a worker-thread completion routine, with an outstanding reference to 'Interfacep' from the time the read-operation was begun. --*/ { PVOID Context; PVOID Context2; ULONG Error; PDNS_HEADER Headerp; PDNS_QUERY Queryp; SOCKET Socket; PROFILE("DnsProcessResponseMessage"); #if DBG NhDump( TRACE_FLAG_DNS, Bufferp->Buffer, Bufferp->BytesTransferred, 1 ); #endif InterlockedIncrement( reinterpret_cast(&DnsStatistics.ResponsesReceived) ); Headerp = (PDNS_HEADER)Bufferp->Buffer; Socket = Bufferp->Socket; Context = Bufferp->Context; Context2 = Bufferp->Context2; // // Reference the interface and attempt to forward the response // EnterCriticalSection(&DnsInterfaceLock); if (!DNS_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&DnsInterfaceLock); } else { LeaveCriticalSection(&DnsInterfaceLock); ACQUIRE_LOCK(Interfacep); // // See if the response is for a pending query // if (!(Queryp = DnsMapResponseToQuery(Interfacep, Headerp->Xid))) { RELEASE_LOCK(Interfacep); DNS_DEREFERENCE_INTERFACE(Interfacep); InterlockedIncrement( reinterpret_cast(&DnsStatistics.MessagesIgnored) ); } else { // // We have the corresponding query. // Send the response back to the client. // Headerp->Xid = Queryp->SourceId; Error = NhWriteDatagramSocket( &DnsComponentReference, Bufferp->Socket, Queryp->SourceAddress, Queryp->SourcePort, Bufferp, Bufferp->BytesTransferred, DnsWriteCompletionRoutine, Interfacep, (PVOID)Queryp->QueryId ); RELEASE_LOCK(Interfacep); // // This buffer is in use for a send-operation, // so don't repost it below. // if (!Error) { Bufferp = NULL; InterlockedIncrement( reinterpret_cast(&DnsStatistics.ResponsesSent) ); } else { DNS_DEREFERENCE_INTERFACE(Interfacep); NhWarningLog( IP_DNS_PROXY_LOG_RESPONSE_FAILED, Error, "%I", NhQueryAddressSocket(Socket) ); } } } // // Post another read buffer // EnterCriticalSection(&DnsInterfaceLock); if (!DNS_REFERENCE_INTERFACE(Interfacep)) { LeaveCriticalSection(&DnsInterfaceLock); if (Bufferp) { NhReleaseBuffer(Bufferp); } } else { LeaveCriticalSection(&DnsInterfaceLock); do { Error = NhReadDatagramSocket( &DnsComponentReference, Socket, Bufferp, DnsReadCompletionRoutine, Context, Context2 ); // // A connection-reset error indicates that our last *send* // could not be delivered at its destination. // We could hardly care less; so issue the read again, // immediately. // } while (Error == WSAECONNRESET); if (Error) { ACQUIRE_LOCK(Interfacep); DnsDeferReadInterface(Interfacep, Socket); RELEASE_LOCK(Interfacep); DNS_DEREFERENCE_INTERFACE(Interfacep); if (Bufferp) { NhReleaseBuffer(Bufferp); } NhErrorLog( IP_DNS_PROXY_LOG_RECEIVE_FAILED, Error, "%I", NhQueryAddressSocket(Socket) ); } } } // DnsProcessResponseMessage