1434 lines
37 KiB
C
1434 lines
37 KiB
C
/*++
|
||
|
||
Copyright (c) 1999-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
mcast.c
|
||
|
||
Abstract:
|
||
|
||
DNS Resolver Service
|
||
|
||
Multicast routines.
|
||
|
||
Author:
|
||
|
||
Glenn Curtis (glennc) December 1999
|
||
|
||
Revision History:
|
||
|
||
James Gilroy (jamesg) February 2000 cleanup
|
||
|
||
--*/
|
||
|
||
|
||
#include "local.h"
|
||
|
||
|
||
//
|
||
// Globals
|
||
//
|
||
|
||
HANDLE g_hMulticastThread = NULL;
|
||
|
||
BOOL g_MulticastStop = FALSE;
|
||
SOCKET g_MulticastUnicastSocket = 0;
|
||
PSOCKET_CONTEXT g_MulticastIoContextList = NULL;
|
||
|
||
HANDLE g_MulticastCompletionPort = NULL;
|
||
|
||
//
|
||
// Should be read from netinfo blob
|
||
//
|
||
PSTR g_HostName = NULL;
|
||
|
||
|
||
//
|
||
// Multicast config globals
|
||
//
|
||
// DCR: regkeys for multicast values
|
||
//
|
||
|
||
#define DNS_DEFAULT_ALLOW_MULTICAST_RESOLVER_OPERATION 0 // Off
|
||
#define DNS_DEFAULT_ALLOW_MULTICAST_RESOLVER_AS_PROXY 0 // Off
|
||
#define DNS_DEFAULT_ALLOW_MULTICAST_DNS_SRV_RECORD 0 // Off
|
||
#define DNS_DEFAULT_MULTICAST_RESOLVER_RECORD_TTL 10*60 // 10 minutes
|
||
|
||
DWORD g_MulticastRecordTTL = DNS_DEFAULT_MULTICAST_RESOLVER_RECORD_TTL;
|
||
BOOL g_AllowMulticastAsProxy = FALSE;
|
||
BOOL g_AllowMulticastDnsSrvRecord = FALSE;
|
||
|
||
|
||
|
||
//
|
||
// Private prototypes
|
||
//
|
||
|
||
BOOL
|
||
IsLocalMachineQuery(
|
||
IN LPSTR pName,
|
||
IN WORD Type,
|
||
OUT PDNS_RECORD * ppRecord
|
||
);
|
||
|
||
VOID
|
||
FixupNameOwnerPointers(
|
||
IN OUT PDNS_RECORD pRecord
|
||
);
|
||
|
||
PDNS_RECORD
|
||
BuildPTR(
|
||
IN LPSTR pName,
|
||
IN LPSTR pHostname,
|
||
IN LPSTR pDomain,
|
||
IN LPSTR pPrimaryDomain
|
||
);
|
||
|
||
PDNS_RECORD
|
||
BuildARecord(
|
||
IN LPSTR Name,
|
||
IN IP_ADDRESS Address );
|
||
|
||
PDNS_RECORD
|
||
BuildDNSServerRecord(
|
||
IN IP_ADDRESS Address );
|
||
|
||
PDNS_RECORD
|
||
BuildLocalAddressRecords(
|
||
IN LPSTR Name );
|
||
|
||
BOOL
|
||
IsLocalAddress(
|
||
IN IP_ADDRESS Ip );
|
||
|
||
PSOCKET_CONTEXT
|
||
AllocateIoContext(
|
||
IN IP_ADDRESS ipAddr );
|
||
|
||
VOID
|
||
FreeIoContextList(
|
||
IN PSOCKET_CONTEXT pContext,
|
||
IN BOOL fIssueSocketShutdown );
|
||
|
||
VOID
|
||
DropReceive(
|
||
IN PSOCKET_CONTEXT pContext );
|
||
|
||
|
||
|
||
|
||
|
||
VOID
|
||
MulticastThread(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Multicast response thread.
|
||
|
||
Runs while cache is running, responding to multicast queries.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
DNS_STATUS status = NO_ERROR;
|
||
PDNS_NETINFO ptempNetInfo = NULL;
|
||
PSOCKET_CONTEXT pcontext;
|
||
DWORD iter;
|
||
DWORD bytesRecvd;
|
||
LPOVERLAPPED poverlapped;
|
||
|
||
//
|
||
// init globals for safe cleanup on failure
|
||
//
|
||
|
||
g_MulticastStop = FALSE;
|
||
g_MulticastCompletionPort = NULL;
|
||
g_MulticastUnicastSocket = 0;
|
||
g_MulticastIoContextList = NULL;
|
||
|
||
//
|
||
// We are going to create a completion port with
|
||
// socket contexts for each of the primary IP addresses of each
|
||
// adapter on the system.
|
||
//
|
||
|
||
//
|
||
// Get a copy of the network adapter information
|
||
//
|
||
|
||
ptempNetInfo = GrabNetworkInfo();
|
||
if ( ! ptempNetInfo )
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// create multicast completion port
|
||
//
|
||
|
||
g_MulticastCompletionPort = CreateIoCompletionPort(
|
||
INVALID_HANDLE_VALUE,
|
||
NULL,
|
||
0,
|
||
0 );
|
||
if ( ! g_MulticastCompletionPort )
|
||
{
|
||
DNSLOG_F1( "Error: Failed to create io completion port." );
|
||
goto Cleanup;
|
||
}
|
||
|
||
// We create two sockets here, one for multicast receives and
|
||
// one for the unicast response. This shouldn't be necessary;
|
||
// there should be nothing preventing us from responding via
|
||
// unicast even with a socket that's joined to a multicast address.
|
||
// In most cases, it does in fact work; but it seems that
|
||
// packets sent to the local address do not arrive if they are
|
||
// sent from a socket that is configured for multicast receive.
|
||
// Apparently, such a socket sends all packets onto the wire
|
||
// without checking the destination address. I imagine this is
|
||
// a bug in the TCP stack, perhaps, but for now, creating two
|
||
// separate sockets is a simple workaround. - tbrown 10/19/99
|
||
|
||
g_MulticastUnicastSocket = Dns_CreateSocket(
|
||
SOCK_DGRAM,
|
||
INADDR_ANY,
|
||
DNS_PORT_NET_ORDER );
|
||
|
||
if ( g_MulticastUnicastSocket == 0 ||
|
||
g_MulticastUnicastSocket == INVALID_SOCKET )
|
||
{
|
||
DNSLOG_F1( "Error: Failed to create unicast socket." );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// build context list
|
||
// - create socket\context for each adapters first IP
|
||
// - associate socket with completion port
|
||
//
|
||
|
||
g_MulticastIoContextList = NULL;
|
||
pcontext = NULL;
|
||
|
||
for ( iter = 0; iter < ptempNetInfo->cAdapterCount; iter++ )
|
||
{
|
||
PIP_ARRAY pipArray;
|
||
|
||
pipArray = ptempNetInfo->AdapterArray[iter]->pAdapterIPAddresses;
|
||
|
||
if ( pipArray &&
|
||
pipArray->AddrCount )
|
||
{
|
||
PSOCKET_CONTEXT pnewContext;
|
||
|
||
pnewContext = AllocateIoContext( pipArray->AddrArray[0] );
|
||
if ( pnewContext )
|
||
{
|
||
HANDLE hport = NULL;
|
||
|
||
if ( pcontext )
|
||
{
|
||
pcontext->pNext = pnewContext;
|
||
pcontext = pnewContext;
|
||
}
|
||
else
|
||
{
|
||
g_MulticastIoContextList = pnewContext;
|
||
pcontext = pnewContext;
|
||
}
|
||
|
||
hport = CreateIoCompletionPort(
|
||
(HANDLE) pnewContext->Socket,
|
||
g_MulticastCompletionPort,
|
||
(UINT_PTR) pnewContext,
|
||
0 );
|
||
|
||
if ( !hport )
|
||
{
|
||
DNSLOG_F1( "Error: Failed to add socket to io completion port." );
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// if no sockets -- done
|
||
//
|
||
|
||
if ( ! g_MulticastIoContextList )
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// drop listen on sockets
|
||
//
|
||
|
||
pcontext = g_MulticastIoContextList;
|
||
while ( pcontext )
|
||
{
|
||
DropReceive( pcontext );
|
||
pcontext = pcontext->pNext;
|
||
}
|
||
|
||
//
|
||
// main listen loop
|
||
//
|
||
|
||
do
|
||
{
|
||
if ( g_LogTraceInfo )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Going to listen for message." );
|
||
DNSLOG_F1( "" );
|
||
}
|
||
|
||
if ( GetQueuedCompletionStatus(
|
||
g_MulticastCompletionPort,
|
||
& bytesRecvd,
|
||
& (ULONG_PTR) pcontext,
|
||
& poverlapped,
|
||
INFINITE ) )
|
||
{
|
||
//
|
||
// We received notice that something happened on a given
|
||
// completion port, get the socket from the context and
|
||
// do a receive to see what we got.
|
||
//
|
||
|
||
if ( pcontext && bytesRecvd )
|
||
{
|
||
WORD Xid;
|
||
IP4_ADDRESS Ip;
|
||
BYTE Opcode;
|
||
WORD QuestionCount;
|
||
WORD AnswerCount;
|
||
WORD NameServerCount;
|
||
WORD AdditionalCount;
|
||
|
||
Ip = MSG_REMOTE_IP4( pcontext->pMsg );
|
||
Xid = pcontext->pMsg->MessageHead.Xid;
|
||
Opcode = pcontext->pMsg->MessageHead.Opcode;
|
||
QuestionCount = pcontext->pMsg->MessageHead.QuestionCount;
|
||
AnswerCount = pcontext->pMsg->MessageHead.AnswerCount;
|
||
NameServerCount = pcontext->pMsg->MessageHead.NameServerCount;
|
||
AdditionalCount = pcontext->pMsg->MessageHead.AdditionalCount;
|
||
|
||
if ( g_LogTraceInfo )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Received DNS message." );
|
||
DNSLOG_F1( " Message contained . . ." );
|
||
DNSLOG_F2( " Xid : 0x%x", Xid );
|
||
DNSLOG_F2( " IP Address : %s", IP_STRING( Ip ) );
|
||
DNSLOG_F2( " Opcode : 0x%x", Opcode );
|
||
DNSLOG_F1( "" );
|
||
}
|
||
|
||
if ( g_MulticastStop )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver detected stop signal." );
|
||
DNSLOG_F1( " Will not process last received message." );
|
||
DNSLOG_F1( "" );
|
||
continue;
|
||
}
|
||
|
||
if ( IsLocalAddress( Ip ) )
|
||
{
|
||
if ( g_LogTraceInfo )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Skip query from local machine." );
|
||
DNSLOG_F1( "" );
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if ( Opcode == DNS_OPCODE_QUERY &&
|
||
QuestionCount == 1 &&
|
||
AnswerCount == 0 &&
|
||
NameServerCount == 0 &&
|
||
AdditionalCount == 0 )
|
||
{
|
||
char QuestionName[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
||
WORD QuestionNameLength = DNS_MAX_NAME_BUFFER_LENGTH;
|
||
PCHAR pch;
|
||
WORD QuestionType = 0;
|
||
WORD QuestionClass = 0;
|
||
PDNS_RECORD pRecord = NULL;
|
||
|
||
pch = (PCHAR) &pcontext->pMsg->MessageBody;
|
||
|
||
//
|
||
// Get DNS Question name from packet
|
||
//
|
||
pch = Dns_ReadPacketName(
|
||
QuestionName,
|
||
&QuestionNameLength,
|
||
0,
|
||
0,
|
||
pch,
|
||
(PCHAR) &pcontext->pMsg->MessageHead,
|
||
pcontext->pMsg->pBufferEnd );
|
||
|
||
if ( !pch )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Dns_ReadPacketName failed" );
|
||
DNSLOG_F2( " with error: 0x%x" , DNS_RCODE_FORMAT_ERROR );
|
||
DNSLOG_F1( "" );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Get DNS Question type from packet
|
||
//
|
||
QuestionType = ntohs( *(UNALIGNED WORD *) pch );
|
||
pch += sizeof( WORD );
|
||
|
||
//
|
||
// Get DNS Question class from packet
|
||
//
|
||
QuestionClass = ntohs( *(UNALIGNED WORD *) pch );
|
||
pch += sizeof( WORD );
|
||
|
||
DNSLOG_F1( " Multicast Resolver - Valid query for . . ." );
|
||
DNSLOG_F2( " Name : %s", QuestionName );
|
||
DNSLOG_F2( " Type : %d", QuestionType );
|
||
DNSLOG_F2( " Class : %d", QuestionClass );
|
||
DNSLOG_F1( "" );
|
||
|
||
//
|
||
// Set pCurrent so that question section is included
|
||
// in response packet.
|
||
//
|
||
pcontext->pMsg->pCurrent = pch;
|
||
|
||
pcontext->pMsg->MessageHead.IsResponse = TRUE;
|
||
pcontext->pMsg->MessageHead.ResponseCode = 0;
|
||
pcontext->pMsg->MessageHead.RecursionAvailable = 0;
|
||
pcontext->pMsg->MessageHead.Authoritative = 0;
|
||
|
||
if ( IsLocalMachineQuery( QuestionName,
|
||
QuestionType,
|
||
&pRecord ) )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Got valid local machine query" );
|
||
pcontext->pMsg->MessageHead.Authoritative = TRUE;
|
||
|
||
status = Dns_AddRecordsToMessage( pcontext->pMsg,
|
||
pRecord,
|
||
FALSE );
|
||
|
||
Dns_RecordListFree( pRecord );
|
||
|
||
// Switch to the unicast socket
|
||
pcontext->pMsg->Socket = g_MulticastUnicastSocket;
|
||
|
||
Dns_Send( pcontext->pMsg );
|
||
}
|
||
else if ( g_AllowMulticastAsProxy )
|
||
{
|
||
LPSTR lpTempName = NULL;
|
||
WORD wNameLen;
|
||
|
||
DNSLOG_F1( " Multicast Resolver: Going to proxy query for client" );
|
||
|
||
wNameLen = (WORD) strlen( QuestionName );
|
||
|
||
lpTempName = MCAST_HEAP_ALLOC_ZERO(
|
||
(wNameLen + 1) * sizeof(WCHAR) );
|
||
|
||
if ( lpTempName == NULL )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: HeapAlloc failed" );
|
||
continue;
|
||
}
|
||
|
||
Dns_NameCopy( lpTempName,
|
||
NULL,
|
||
QuestionName,
|
||
wNameLen,
|
||
DnsCharSetUtf8,
|
||
DnsCharSetUnicode );
|
||
|
||
status = R_ResolverQuery(
|
||
NULL,
|
||
(LPWSTR) lpTempName,
|
||
QuestionType,
|
||
0,
|
||
&pRecord );
|
||
|
||
MCAST_HEAP_FREE( lpTempName );
|
||
|
||
if ( status == DNS_ERROR_RCODE_NAME_ERROR )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: returning DNS_RCODE_NAME_ERROR" );
|
||
pcontext->pMsg->MessageHead.ResponseCode = DNS_RCODE_NAME_ERROR;
|
||
|
||
// Switch to the unicast socket
|
||
pcontext->pMsg->Socket = g_MulticastUnicastSocket;
|
||
|
||
Dns_Send( pcontext->pMsg );
|
||
}
|
||
else if ( status == DNS_ERROR_NAME_DOES_NOT_EXIST )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: returning DNS_RCODE_NXDOMAIN" );
|
||
pcontext->pMsg->MessageHead.ResponseCode = DNS_RCODE_NXDOMAIN;
|
||
|
||
// Switch to the unicast socket
|
||
pcontext->pMsg->Socket = g_MulticastUnicastSocket;
|
||
|
||
Dns_Send( pcontext->pMsg );
|
||
}
|
||
else if ( status == DNS_ERROR_RCODE_NXRRSET )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: returning DNS_RCODE_NXRRSET" );
|
||
pcontext->pMsg->MessageHead.ResponseCode = DNS_RCODE_NXRRSET;
|
||
|
||
// Switch to the unicast socket
|
||
pcontext->pMsg->Socket = g_MulticastUnicastSocket;
|
||
|
||
Dns_Send( pcontext->pMsg );
|
||
}
|
||
else if ( status == NO_ERROR )
|
||
{
|
||
if ( pRecord )
|
||
{
|
||
FixupNameOwnerPointers( pRecord );
|
||
|
||
status = Dns_AddRecordsToMessage(
|
||
pcontext->pMsg,
|
||
pRecord,
|
||
FALSE );
|
||
|
||
Dns_RecordListFree( pRecord );
|
||
|
||
if ( status != ERROR_SUCCESS )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Dns_AddRecordsToMessage failed" );
|
||
DNSLOG_F2( " with error: 0x%x" , status );
|
||
continue;
|
||
}
|
||
|
||
// Switch to the unicast socket
|
||
pcontext->pMsg->Socket = g_MulticastUnicastSocket;
|
||
|
||
Dns_Send( pcontext->pMsg );
|
||
}
|
||
}
|
||
else if ( pRecord )
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Nothing to return" );
|
||
Dns_RecordListFree( pRecord );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: Nothing can be done for this client query" );
|
||
}
|
||
|
||
DNSLOG_F1( "" );
|
||
}
|
||
|
||
//
|
||
// Now send down another receive request to wait on.
|
||
//
|
||
DropReceive( pcontext );
|
||
}
|
||
else
|
||
{
|
||
DNSLOG_F1( " Multicast Resolver: GQCP returned no context!" );
|
||
DNSLOG_F1( " Could be terminating resolver service?" );
|
||
DNSLOG_F1( "" );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// GQCP failed, see what the failure was and handle accordingly
|
||
//
|
||
status = GetLastError();
|
||
|
||
if ( !pcontext )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Now send down another receive request to wait on.
|
||
//
|
||
DropReceive( pcontext );
|
||
}
|
||
}
|
||
while( !g_MulticastStop );
|
||
|
||
|
||
Cleanup :
|
||
|
||
//
|
||
// cleanup multicast stuff
|
||
// - sockets, contexts, i/o completion port
|
||
//
|
||
|
||
NetInfo_Free( ptempNetInfo );
|
||
|
||
if ( g_MulticastUnicastSocket )
|
||
{
|
||
Dns_CloseSocket( g_MulticastUnicastSocket );
|
||
g_MulticastUnicastSocket = 0;
|
||
}
|
||
|
||
FreeIoContextList( g_MulticastIoContextList, FALSE );
|
||
g_MulticastIoContextList = NULL;
|
||
|
||
if ( g_MulticastCompletionPort )
|
||
{
|
||
CloseHandle( g_MulticastCompletionPort );
|
||
g_MulticastCompletionPort = 0;
|
||
}
|
||
|
||
DNSLOG_F1( "MulticastThread exiting." );
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
MulticastThreadSignalStop(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Stop the multicast thread.
|
||
|
||
Note, not synchronous, simply signals shutdown.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
g_MulticastStop = TRUE;
|
||
|
||
PostQueuedCompletionStatus(
|
||
g_MulticastCompletionPort,
|
||
0,
|
||
0,
|
||
NULL );
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
IsLocalMachineQuery(
|
||
IN LPSTR pName,
|
||
IN WORD Type,
|
||
OUT PDNS_RECORD * ppRecord
|
||
)
|
||
{
|
||
PDNS_NETINFO ptempNetworkInfo = NULL;
|
||
LPSTR pszPrimaryDomain = NULL;
|
||
DWORD iter;
|
||
DWORD iter2;
|
||
PDNS_RECORD presultRecords = NULL;
|
||
|
||
|
||
//
|
||
// if no hostname -- bail
|
||
//
|
||
// note: MUST INSURE hostname is never NULL afterwards or
|
||
// (preferred) take local copy under lock
|
||
//
|
||
|
||
if ( !g_HostName )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// get local copy of network info
|
||
//
|
||
// DCR_FIX: cleanup net-info creation
|
||
//
|
||
|
||
ptempNetworkInfo = GrabNetworkInfo();
|
||
if ( ! ptempNetworkInfo )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
// get primary domain name -- if any
|
||
|
||
if ( ptempNetworkInfo->pSearchList )
|
||
{
|
||
pszPrimaryDomain = ptempNetworkInfo->pSearchList->pszDomainOrZoneName;
|
||
}
|
||
|
||
|
||
//
|
||
// A record query
|
||
//
|
||
|
||
if ( Type == DNS_TYPE_A )
|
||
{
|
||
char testName[DNS_MAX_NAME_LENGTH * 2];
|
||
|
||
//
|
||
// host name with PDN if available
|
||
//
|
||
|
||
strcpy( testName, g_HostName );
|
||
if ( pszPrimaryDomain )
|
||
{
|
||
strcat( testName, "." );
|
||
strcat( testName, pszPrimaryDomain );
|
||
}
|
||
|
||
// name matches hostname -- return name (with PDN if available)
|
||
|
||
if ( Dns_NameCompare_UTF8( pName, g_HostName ) )
|
||
{
|
||
presultRecords = BuildLocalAddressRecords( testName );
|
||
goto Done;
|
||
}
|
||
|
||
// name matches full PDN -- return full PDN
|
||
|
||
if ( pszPrimaryDomain &&
|
||
Dns_NameCompare_UTF8( pName, testName ) )
|
||
{
|
||
presultRecords = BuildLocalAddressRecords( testName );
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// is name ".local" query
|
||
//
|
||
|
||
strcpy( testName, g_HostName );
|
||
strcat( testName, "." );
|
||
strcat( testName, MULTICAST_DNS_LOCAL_DOMAIN );
|
||
|
||
if ( Dns_NameCompare_UTF8( pName, testName ) )
|
||
{
|
||
presultRecords = BuildLocalAddressRecords( testName );
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// see if name matches any adapter name
|
||
//
|
||
|
||
for ( iter = 0; iter < ptempNetworkInfo->cAdapterCount; iter++ )
|
||
{
|
||
PIP_ARRAY pipArray = ptempNetworkInfo->AdapterArray[iter]->
|
||
pAdapterIPAddresses;
|
||
LPSTR pszDomain = ptempNetworkInfo->AdapterArray[iter]->
|
||
pszAdapterDomain;
|
||
|
||
if ( pszDomain )
|
||
{
|
||
strcpy( testName, g_HostName );
|
||
strcat( testName, "." );
|
||
strcat( testName, pszDomain );
|
||
|
||
if ( pipArray &&
|
||
Dns_NameCompare_UTF8( pName, testName ) )
|
||
{
|
||
PDNS_RECORD pPrev = NULL;
|
||
|
||
for ( iter2 = 0; iter2 < pipArray->AddrCount; iter2++ )
|
||
{
|
||
PDNS_RECORD pNew = BuildARecord(
|
||
testName,
|
||
pipArray->AddrArray[iter2] );
|
||
if ( pNew )
|
||
{
|
||
if ( pPrev )
|
||
pPrev->pNext = pNew;
|
||
else
|
||
presultRecords = pNew;
|
||
|
||
pPrev = pNew;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// PTR query
|
||
//
|
||
|
||
else if ( Type == DNS_TYPE_PTR )
|
||
{
|
||
//
|
||
// check for loopback
|
||
// Dnslib uses this address to 'ping' adapters, must always respond
|
||
//
|
||
|
||
if ( Dns_NameCompare_UTF8( pName, "1.0.0.127.in-addr.arpa." ) )
|
||
{
|
||
presultRecords = BuildPTR(
|
||
pName,
|
||
g_HostName,
|
||
NULL,
|
||
pszPrimaryDomain );
|
||
goto Done;
|
||
}
|
||
|
||
//
|
||
// check all IPs on box (one adapter at a time)
|
||
//
|
||
|
||
for ( iter = 0; iter < ptempNetworkInfo->cAdapterCount; iter++ )
|
||
{
|
||
PIP_ARRAY pipArray = ptempNetworkInfo ->
|
||
AdapterArray[iter] ->
|
||
pAdapterIPAddresses;
|
||
LPSTR pszDomain = ptempNetworkInfo ->
|
||
AdapterArray[iter] ->
|
||
pszAdapterDomain;
|
||
|
||
if ( pipArray )
|
||
{
|
||
// check if query matches IP for this adapter
|
||
//
|
||
// ENHANCE: this is backwards; ought to covert query to IP
|
||
// and then check IPs, rather than turning every IP into
|
||
// reverse name; then could even use standard routines
|
||
// to build PTR
|
||
|
||
for ( iter2 = 0; iter2 < pipArray->AddrCount; iter2++ )
|
||
{
|
||
CHAR reverseName[DNS_MAX_REVERSE_NAME_BUFFER_LENGTH];
|
||
IP4_ADDRESS ip = pipArray->AddrArray[iter2];
|
||
|
||
Dns_Ip4AddressToReverseName_A(
|
||
reverseName,
|
||
ip );
|
||
|
||
if ( Dns_NameCompare_UTF8( pName, reverseName ) )
|
||
{
|
||
presultRecords = BuildPTR(
|
||
pName,
|
||
g_HostName,
|
||
pszDomain,
|
||
pszPrimaryDomain );
|
||
goto Done;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// SRV query
|
||
//
|
||
|
||
else if ( Type == DNS_TYPE_SRV )
|
||
{
|
||
//
|
||
// check if ".local" query
|
||
//
|
||
|
||
if ( Dns_NameCompare_UTF8( pName, MULTICAST_DNS_SRV_RECORD_NAME ) )
|
||
{
|
||
if ( g_AllowMulticastDnsSrvRecord &&
|
||
ptempNetworkInfo->cAdapterCount &&
|
||
ptempNetworkInfo->AdapterArray[0]->cServerCount )
|
||
{
|
||
presultRecords = BuildDNSServerRecord(
|
||
ptempNetworkInfo->
|
||
AdapterArray[0]->
|
||
ServerArray[0].IpAddress );
|
||
goto Done;
|
||
}
|
||
}
|
||
}
|
||
|
||
Done:
|
||
|
||
// cleanup
|
||
// return records
|
||
// if found records TRUE; otherwise FALSE
|
||
|
||
NetInfo_Free( ptempNetworkInfo );
|
||
|
||
*ppRecord = presultRecords;
|
||
|
||
return( presultRecords != NULL );
|
||
}
|
||
|
||
|
||
VOID
|
||
FixupNameOwnerPointers(
|
||
IN OUT PDNS_RECORD pRpcRecord
|
||
)
|
||
{
|
||
PDNS_RECORD pTempRecord = pRpcRecord;
|
||
LPWSTR lpNameOwner = pRpcRecord->pName;
|
||
|
||
DNSDBG( TRACE, ( "FixupNameOwnerPointers()\n" ));
|
||
|
||
while ( pTempRecord )
|
||
{
|
||
if ( pTempRecord->pName == NULL )
|
||
pTempRecord->pName = lpNameOwner;
|
||
else
|
||
lpNameOwner = pTempRecord->pName;
|
||
|
||
pTempRecord = pTempRecord->pNext;
|
||
}
|
||
}
|
||
|
||
|
||
PDNS_RECORD
|
||
BuildPTR(
|
||
IN LPSTR pName,
|
||
IN LPSTR pHostname,
|
||
IN LPSTR pDomain,
|
||
IN LPSTR pPrimaryDomain
|
||
)
|
||
{
|
||
PDNS_RECORD prr1 = NULL;
|
||
PDNS_RECORD prr2 = NULL;
|
||
LPSTR pdata1 = NULL;
|
||
LPSTR pdata2 = NULL;
|
||
LPSTR pnameOwner = NULL;
|
||
DWORD length;
|
||
|
||
//
|
||
// DCR_FIX: duplicates a lot of Dns_CreatePtrRecord
|
||
//
|
||
// DCR_FIX: build generic name-appending counter and
|
||
// builder
|
||
//
|
||
// DCR_FIX0: records are not unicode is this ok?
|
||
//
|
||
|
||
prr1 = Dns_AllocateRecord( sizeof( DNS_PTR_DATA ) );
|
||
if ( !prr1 )
|
||
{
|
||
return( NULL );
|
||
}
|
||
|
||
//
|
||
// create owner name
|
||
// create data name
|
||
//
|
||
|
||
pnameOwner = RECORD_HEAP_ALLOC( strlen(pName) + 1 );
|
||
if ( !pnameOwner )
|
||
{
|
||
goto Failed;
|
||
}
|
||
strcpy( pnameOwner, pName );
|
||
|
||
//
|
||
// create PTR data
|
||
//
|
||
|
||
length = strlen( pHostname ) + 2;
|
||
if ( pDomain )
|
||
{
|
||
length += strlen( pDomain ) + 2;
|
||
}
|
||
strcpy( pdata1, pHostname );
|
||
strcat( pdata1, "." );
|
||
if ( pDomain )
|
||
{
|
||
strcat( pdata1, pDomain );
|
||
}
|
||
|
||
//
|
||
// create, fill-in record
|
||
//
|
||
|
||
pdata1 = RECORD_HEAP_ALLOC( length );
|
||
if ( !pdata1 )
|
||
{
|
||
goto Failed;
|
||
}
|
||
|
||
prr1->pNext = NULL;
|
||
prr1->pName = (PDNS_NAME) pnameOwner;
|
||
prr1->wType = DNS_TYPE_PTR;
|
||
prr1->Flags.S.Section = DNSREC_ANSWER;
|
||
SET_FREE_DATA( prr1 );
|
||
SET_FREE_OWNER( prr1 );
|
||
prr1->dwTtl = g_MulticastRecordTTL;
|
||
prr1->Data.Ptr.pNameHost = (PDNS_NAME) pdata1;
|
||
|
||
//
|
||
// build record for primary domain name
|
||
// - if exists and different from domain name
|
||
//
|
||
|
||
if ( pPrimaryDomain &&
|
||
! Dns_NameCompare_UTF8( pDomain, pPrimaryDomain ) )
|
||
{
|
||
pdata2 = RECORD_HEAP_ALLOC(
|
||
strlen( pHostname ) + strlen( pPrimaryDomain ) + 2 );
|
||
if ( !pdata2 )
|
||
{
|
||
goto Failed;
|
||
}
|
||
strcpy( pdata2, pHostname );
|
||
strcat( pdata2, "." );
|
||
strcat( pdata2, pPrimaryDomain );
|
||
|
||
prr2 = Dns_AllocateRecord( sizeof( DNS_PTR_DATA ) );
|
||
if ( !prr2 )
|
||
{
|
||
goto Failed;
|
||
}
|
||
prr2->pNext = NULL;
|
||
prr2->pName = (PDNS_NAME) pnameOwner;
|
||
prr2->wType = DNS_TYPE_PTR;
|
||
prr2->Flags.S.Section = DNSREC_ANSWER;
|
||
SET_FREE_DATA( prr2 );
|
||
prr2->dwTtl = g_MulticastRecordTTL;
|
||
prr2->Data.Ptr.pNameHost = (PDNS_NAME) pdata2;
|
||
}
|
||
|
||
//
|
||
// set next field
|
||
// - appends record for primary name (if any)
|
||
// - or sets pNext=NULL
|
||
//
|
||
|
||
prr1->pNext = prr2;
|
||
|
||
return prr1;
|
||
|
||
|
||
Failed:
|
||
|
||
RECORD_HEAP_FREE( prr1 );
|
||
RECORD_HEAP_FREE( pnameOwner );
|
||
RECORD_HEAP_FREE( pdata1 );
|
||
RECORD_HEAP_FREE( prr2 );
|
||
RECORD_HEAP_FREE( pdata2 );
|
||
return NULL;
|
||
}
|
||
|
||
|
||
PDNS_RECORD
|
||
BuildARecord(
|
||
IN LPSTR pName,
|
||
IN IP_ADDRESS IpAddress
|
||
)
|
||
{
|
||
PDNS_RECORD prr;
|
||
LPSTR pnameOwner;
|
||
|
||
prr = Dns_AllocateRecord( sizeof(DNS_A_DATA) );
|
||
if ( !prr )
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
pnameOwner = RECORD_HEAP_ALLOC( strlen(pName) + 1 );
|
||
if ( ! pnameOwner )
|
||
{
|
||
goto Failed;
|
||
}
|
||
|
||
strcpy( pnameOwner, pName );
|
||
|
||
prr->pNext = NULL;
|
||
prr->pName = (PDNS_NAME) pnameOwner;
|
||
prr->wType = DNS_TYPE_A;
|
||
prr->dwTtl = g_MulticastRecordTTL;
|
||
prr->Flags.S.Section = DNSREC_ANSWER;
|
||
SET_FREE_OWNER( prr );
|
||
SET_FREE_DATA( prr );
|
||
prr->Data.A.IpAddress = IpAddress;
|
||
|
||
return prr;
|
||
|
||
|
||
Failed:
|
||
|
||
RECORD_HEAP_FREE( prr );
|
||
return NULL;
|
||
}
|
||
|
||
|
||
PDNS_RECORD
|
||
BuildDNSServerRecord(
|
||
IN IP_ADDRESS IpAddress
|
||
)
|
||
{
|
||
PDNS_RECORD prr = NULL;
|
||
PDNS_RECORD prrAdditional = NULL;
|
||
LPSTR pnameOwner = NULL;
|
||
LPSTR pnameTarget = NULL;
|
||
|
||
|
||
//
|
||
// DCR_FIX: where to start? geez
|
||
//
|
||
// DCR_FIX: character set issue -- what's the paradigm here?
|
||
//
|
||
|
||
//
|
||
// build additional record
|
||
//
|
||
|
||
prrAdditional = BuildARecord( MULTICAST_DNS_A_RECORD_NAME, IpAddress );
|
||
if ( ! prrAdditional )
|
||
{
|
||
goto Failed;
|
||
}
|
||
prrAdditional->Flags.S.Section = DNSREC_ADDITIONAL;
|
||
|
||
|
||
//
|
||
// build SRV record
|
||
//
|
||
|
||
prr = Dns_AllocateRecord( sizeof( DNS_SRV_DATA ) );
|
||
if ( !prr )
|
||
{
|
||
return( NULL );
|
||
}
|
||
|
||
pnameOwner = RECORD_HEAP_ALLOC( strlen(MULTICAST_DNS_SRV_RECORD_NAME) + 1 );
|
||
if ( !pnameOwner )
|
||
{
|
||
goto Failed;
|
||
}
|
||
strcpy( pnameOwner, MULTICAST_DNS_SRV_RECORD_NAME );
|
||
|
||
prr->pNext = prrAdditional;
|
||
prr->pName = (PDNS_NAME) pnameOwner;
|
||
prr->Flags.S.Section = DNSREC_ANSWER;
|
||
SET_FREE_OWNER( prr );
|
||
prr->wType = DNS_TYPE_SRV;
|
||
prr->dwTtl = g_MulticastRecordTTL;
|
||
prr->Data.SRV.pNameTarget = RECORD_HEAP_ALLOC(
|
||
strlen( MULTICAST_DNS_A_RECORD_NAME ) + 1 );
|
||
if ( ! pnameTarget )
|
||
{
|
||
goto Failed;
|
||
}
|
||
strcpy(
|
||
pnameTarget,
|
||
MULTICAST_DNS_A_RECORD_NAME );
|
||
|
||
prr->Data.SRV.wPriority = 0;
|
||
prr->Data.SRV.wWeight = 0;
|
||
prr->Data.SRV.wPort = DNS_PORT_HOST_ORDER;
|
||
prr->Data.SRV.pNameTarget = (PDNS_NAME) pnameTarget;
|
||
SET_FREE_DATA( prr );
|
||
|
||
return prr;
|
||
|
||
|
||
Failed:
|
||
|
||
// cleanup
|
||
// - additional rr done wholesale
|
||
// - SRV record done in pieces
|
||
|
||
Dns_RecordFree( prrAdditional );
|
||
RECORD_HEAP_FREE( pnameOwner );
|
||
RECORD_HEAP_FREE( pnameTarget );
|
||
RECORD_HEAP_FREE( prr );
|
||
return NULL;
|
||
}
|
||
|
||
|
||
|
||
PDNS_RECORD
|
||
BuildLocalAddressRecords(
|
||
IN LPSTR Name )
|
||
{
|
||
return NULL;
|
||
|
||
#if 0
|
||
PDNS_RECORD pList = NULL;
|
||
PDNS_RECORD pPrev = NULL;
|
||
DWORD iter;
|
||
|
||
EnterCriticalSection( &NetworkListCritSec );
|
||
|
||
if ( g_IpAddressList &&
|
||
g_IpAddressListCount )
|
||
{
|
||
for ( iter = 0; iter < g_IpAddressListCount; iter++ )
|
||
{
|
||
PDNS_RECORD pNew = BuildARecord( Name,
|
||
g_IpAddressList[iter].ipAddress );
|
||
|
||
if ( pNew )
|
||
{
|
||
if ( pPrev )
|
||
pPrev->pNext = pNew;
|
||
else
|
||
pList = pNew;
|
||
|
||
pPrev = pNew;
|
||
}
|
||
}
|
||
}
|
||
|
||
LeaveCriticalSection( &NetworkListCritSec );
|
||
|
||
return pList;
|
||
#endif
|
||
}
|
||
|
||
|
||
BOOL
|
||
IsLocalAddress(
|
||
IN IP_ADDRESS IpAddress
|
||
)
|
||
{
|
||
return( FALSE );
|
||
|
||
#if 0
|
||
DWORD iter;
|
||
BOOL bisLocal = FALSE;
|
||
|
||
EnterCriticalSection( &NetworkListCritSec );
|
||
|
||
if ( g_IpAddressList )
|
||
{
|
||
for ( iter = 0; iter < g_IpAddressListCount; iter++ )
|
||
{
|
||
if ( IpAddress == g_IpAddressList[iter].ipAddress )
|
||
{
|
||
bisLocal = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
LeaveCriticalSection( &NetworkListCritSec );
|
||
|
||
return bisLocal;
|
||
#endif
|
||
}
|
||
|
||
|
||
PSOCKET_CONTEXT
|
||
AllocateIoContext(
|
||
IN IP_ADDRESS IpAddr
|
||
)
|
||
{
|
||
PSOCKET_CONTEXT pcontext;
|
||
SOCKET socket = 0;
|
||
|
||
|
||
// allocate and clear context
|
||
|
||
pcontext = MCAST_HEAP_ALLOC_ZERO( sizeof(SOCKET_CONTEXT) );
|
||
if ( !pcontext )
|
||
{
|
||
DNSLOG_F1( "Error: Failed to allocate multicast socket context." );
|
||
return NULL;
|
||
}
|
||
|
||
// create multicast socket
|
||
// - bound to this address and DNS port
|
||
|
||
socket = Dns_CreateMulticastSocket(
|
||
SOCK_DGRAM,
|
||
IpAddr,
|
||
DNS_PORT_NET_ORDER,
|
||
FALSE,
|
||
TRUE );
|
||
|
||
if ( socket == 0 || socket == INVALID_SOCKET )
|
||
{
|
||
DNSLOG_F1( "Error: Failed to create multicast socket." );
|
||
goto Failed;
|
||
}
|
||
|
||
// create message buffer for socket
|
||
|
||
pcontext->pMsg = Dns_AllocateMsgBuf( 0 );
|
||
if ( !pcontext->pMsg )
|
||
{
|
||
DNSLOG_F1( "Error: Failed to allocate message buffer." );
|
||
goto Failed;
|
||
}
|
||
|
||
pcontext->Socket = socket;
|
||
pcontext->IpAddress = IpAddr;
|
||
pcontext->pMsg->fTcp = FALSE;
|
||
|
||
return pcontext;
|
||
|
||
Failed:
|
||
|
||
if ( socket )
|
||
{
|
||
Dns_CloseSocket( socket );
|
||
}
|
||
MCAST_HEAP_FREE( pcontext );
|
||
return NULL;
|
||
}
|
||
|
||
|
||
VOID
|
||
FreeIoContextList(
|
||
IN OUT PSOCKET_CONTEXT pContext,
|
||
IN BOOL fIssueSocketShutdown
|
||
)
|
||
{
|
||
PSOCKET_CONTEXT ptempContext;
|
||
|
||
//
|
||
// cleanup context list
|
||
// - shutdown socket
|
||
// - close socket
|
||
// - free message buffer
|
||
// - free context
|
||
//
|
||
|
||
while ( pContext )
|
||
{
|
||
ptempContext = pContext;
|
||
pContext = pContext->pNext;
|
||
|
||
if ( fIssueSocketShutdown )
|
||
{
|
||
shutdown( ptempContext->Socket, SD_BOTH );
|
||
}
|
||
|
||
Dns_CloseSocket( ptempContext->Socket );
|
||
|
||
if ( ptempContext->pMsg )
|
||
{
|
||
Dns_Free( ptempContext->pMsg );
|
||
}
|
||
|
||
// QUESTION: should we tag memory free?
|
||
|
||
MCAST_HEAP_FREE( ptempContext );
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
DropReceive(
|
||
IN OUT PSOCKET_CONTEXT pContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Drop down UDP receive request.
|
||
|
||
Arguments:
|
||
|
||
pContext -- context for socket being recieved
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
DNS_STATUS status;
|
||
WSABUF WsaBuf;
|
||
DWORD bytesRecvd;
|
||
DWORD dwFlags = 0;
|
||
|
||
if ( !pContext->pMsg )
|
||
{
|
||
return;
|
||
}
|
||
|
||
WsaBuf.len = DNS_MAX_UDP_PACKET_BUFFER_LENGTH;
|
||
WsaBuf.buf = (PCHAR) (&pContext->pMsg->MessageHead);
|
||
|
||
pContext->pMsg->Socket = pContext->Socket;
|
||
|
||
//
|
||
// loop until successful WSARecvFrom() is down
|
||
//
|
||
// this loop is only active while we continue to recv
|
||
// WSAECONNRESET or WSAEMSGSIZE errors, both of which
|
||
// cause us to dump data and retry;
|
||
//
|
||
// note loop rather than recursion (to this function) is
|
||
// required to aVOID possible stack overflow from malicious
|
||
// send
|
||
//
|
||
// normal returns from WSARecvFrom() are
|
||
// SUCCESS -- packet was waiting, GQCS will fire immediately
|
||
// WSA_IO_PENDING -- no data yet, GQCS will fire when ready
|
||
//
|
||
|
||
while ( 1 )
|
||
{
|
||
status = WSARecvFrom(
|
||
pContext->Socket,
|
||
& WsaBuf,
|
||
1,
|
||
& bytesRecvd,
|
||
& dwFlags,
|
||
(PSOCKADDR) & pContext->pMsg->RemoteAddress,
|
||
& pContext->pMsg->RemoteAddressLength,
|
||
& pContext->Overlapped,
|
||
NULL );
|
||
|
||
if ( status == ERROR_SUCCESS )
|
||
{
|
||
return;
|
||
}
|
||
|
||
status = GetLastError();
|
||
if ( status == WSA_IO_PENDING )
|
||
{
|
||
return;
|
||
}
|
||
|
||
//
|
||
// when last send ICMP'd
|
||
// - set flag to indicate retry and repost send
|
||
// - if over some reasonable number of retries, assume error
|
||
// and fall through recv failure code
|
||
//
|
||
|
||
if ( status == WSAECONNRESET )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if ( status == WSAEMSGSIZE )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// End mcast.c
|
||
//
|