mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1433 lines
37 KiB
1433 lines
37 KiB
/*++
|
|
|
|
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
|
|
//
|