/*++ 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 //