Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1034 lines
20 KiB

/*++
Copyright (c) 2001-2002 Microsoft Corporation
Module Name:
mcast.c
Abstract:
DNS Resolver Service
Multicast routines.
Author:
James Gilroy (jamesg) December 2001
Revision History:
--*/
#include "local.h"
//
// Socket context
//
typedef struct _McastSocketContext
{
struct _SocketContext * pNext;
SOCKET Socket;
PDNS_MSG_BUF pMsg;
BOOL fRecvDown;
OVERLAPPED Overlapped;
}
MCSOCK_CONTEXT, *PMCSOCK_CONTEXT;
//
// Globals
//
HANDLE g_hMcastThread = NULL;
DWORD g_McastThreadId = 0;
BOOL g_McastStop = FALSE;
BOOL g_McastConfigChange = TRUE;
HANDLE g_McastCompletionPort = NULL;
PMCSOCK_CONTEXT g_pMcastContext4 = NULL;
PMCSOCK_CONTEXT g_pMcastContext6 = NULL;
PDNS_NETINFO g_pMcastNetInfo = NULL;
PWSTR g_pwsMcastHostName = NULL;
//
// Mcast config globals
//
#define MCAST_RECORD_TTL 60 // 1 minute
DWORD g_McastRecordTTL = MCAST_RECORD_TTL;
//
// Private prototypes
//
VOID
mcast_ProcessRecv(
IN OUT PMCSOCK_CONTEXT pContext,
IN DWORD BytesRecvd
);
VOID
mcast_FreeIoContext(
IN OUT PMCSOCK_CONTEXT pContext
)
/*++
Routine Description:
Cleanup i/o context.
Includes socket close.
Arguments:
pContext -- context for socket being recieved
Return Value:
None
--*/
{
DNSDBG( TRACE, (
"mcast_FreeIoContext( %p )\n",
pContext ));
//
// cleanup context list
// - close socket
// - free message buffer
// - free context
//
if ( pContext )
{
Socket_Close( pContext->Socket );
MCAST_HEAP_FREE( pContext->pMsg );
MCAST_HEAP_FREE( pContext );
}
}
PMCSOCK_CONTEXT
mcast_CreateIoContext(
IN INT Family,
IN PWSTR pName
)
/*++
Routine Description:
Create and init a i/o context for a protocol.
Arguments:
Family -- address family
pName -- name to be published
Return Value:
NO_ERROR if successful.
ErrorCode on failure.
--*/
{
DNS_STATUS status = NO_ERROR;
PMCSOCK_CONTEXT pcontext = NULL;
SOCKET sock = 0;
DNS_ADDR addr;
HANDLE hport;
//
// setup mcast address
//
status = DnsAddr_BuildMcast(
&addr,
Family,
pName
);
if ( status != NO_ERROR )
{
goto Failed;
}
//
// create multicast socket
// - bound to this address and DNS port
//
sock = Socket_CreateMulticast(
SOCK_DGRAM,
&addr,
MCAST_PORT_NET_ORDER,
FALSE, // not send
TRUE // receive
);
if ( sock == 0 )
{
goto Failed;
}
//
// DCR: mcast context could include message buffer
// (or even just reassign some fields in context
//
// allocate and clear context
pcontext = MCAST_HEAP_ALLOC_ZERO( sizeof(MCSOCK_CONTEXT) );
if ( !pcontext )
{
goto Failed;
}
// create message buffer for socket
pcontext->pMsg = Dns_AllocateMsgBuf( 0 );
if ( !pcontext->pMsg )
{
DNSDBG( ANY, ( "Error: Failed to allocate message buffer." ));
goto Failed;
}
// attach to completion port
hport = CreateIoCompletionPort(
(HANDLE) sock,
g_McastCompletionPort,
(UINT_PTR) pcontext,
0 );
if ( !hport )
{
DNSDBG( INIT, (
"Failed to add socket to io completion port." ));
goto Failed;
}
// fill in context
pcontext->Socket = sock;
sock = 0;
pcontext->pMsg->fTcp = FALSE;
#if 0
// not handling contexts in list yet
// insert into list
InsertTailList( (PLIST_ENTRY)pcontext );
#endif
return pcontext;
Failed:
Socket_Close( sock );
mcast_FreeIoContext( pcontext );
return NULL;
}
VOID
mcast_DropReceive(
IN OUT PMCSOCK_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 flags = 0;
INT retry = 0;
DNSDBG( TRACE, (
"mcast_DropReceive( %p )\n",
pContext ));
if ( !pContext || !pContext->pMsg )
{
DNS_ASSERT( FALSE );
return;
}
//
// DCR: could support larger MCAST packets
//
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 )
{
retry++;
status = WSARecvFrom(
pContext->Socket,
& wsaBuf,
1,
& bytesRecvd,
& flags,
& pContext->pMsg->RemoteAddress.Sockaddr,
& pContext->pMsg->RemoteAddress.SockaddrLength,
& pContext->Overlapped,
NULL );
if ( status == ERROR_SUCCESS )
{
pContext->fRecvDown = TRUE;
return;
}
status = GetLastError();
if ( status == WSA_IO_PENDING )
{
pContext->fRecvDown = TRUE;
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 ||
status == WSAEMSGSIZE )
{
if ( retry > 10 )
{
Sleep( retry );
}
continue;
}
return;
}
}
VOID
mcast_CreateIoContexts(
VOID
)
/*++
Routine Description:
Create any uncreated i/o contexts.
This is run dynamically -- after every mcast recv -- and creates
any missing contexts, hence handling either previous failure or
change in config (name change, protocol change).
Arguments:
None
Return Value:
None
--*/
{
PDNS_NETINFO pnetInfo;
//
// get current netinfo
//
// DCR: should have "do we need new" netinfo check
//
pnetInfo = GrabNetworkInfo();
if ( pnetInfo )
{
NetInfo_Free( g_pMcastNetInfo );
g_pMcastNetInfo = pnetInfo;
}
else
{
pnetInfo = g_pMcastNetInfo;
}
//
// try\retry context setup, to handle dynamic IP\name
//
if ( !g_pMcastContext6 ||
!Dns_NameCompare_W(
g_pwsMcastHostName,
pnetInfo->pszHostName ) )
{
g_pMcastContext6 = mcast_CreateIoContext( AF_INET6, pnetInfo->pszHostName );
Dns_Free( g_pwsMcastHostName );
g_pwsMcastHostName = Dns_CreateStringCopy_W( pnetInfo->pszHostName );
}
if ( !g_pMcastContext4 )
{
g_pMcastContext4 = mcast_CreateIoContext( AF_INET, NULL );
}
//
// redrop receives if not outstanding
//
mcast_DropReceive( g_pMcastContext6 );
mcast_DropReceive( g_pMcastContext4 );
}
VOID
mcast_CleanupIoContexts(
VOID
)
/*++
Routine Description:
Cleanup mcast i/o contexts created.
Arguments:
None
Return Value:
None
--*/
{
mcast_FreeIoContext( g_pMcastContext6 );
mcast_FreeIoContext( g_pMcastContext4 );
g_pMcastContext6 = NULL;
g_pMcastContext4 = NULL;
}
VOID
Mcast_Thread(
VOID
)
/*++
Routine Description:
Mcast response thread.
Runs while cache is running, responding to multicast queries.
Arguments:
None.
Return Value:
None.
--*/
{
DNS_STATUS status = NO_ERROR;
PMCSOCK_CONTEXT pcontext;
DWORD bytesRecvd;
LPOVERLAPPED poverlapped;
BOOL bresult;
DNSDBG( MCAST, ( "Mcast_Thread() start!\n" ));
//
// init mcast globals
//
g_McastStop = FALSE;
g_McastCompletionPort = NULL;
//
// create mcast completion port
//
g_McastCompletionPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
0,
0 );
if ( ! g_McastCompletionPort )
{
DNSDBG( ANY, (
"Error: Failed to create mcast completion port.\n" ));
goto Cleanup;
}
//
// main listen loop
//
while ( 1 )
{
DNSDBG( MCAST, ( "Top of loop!\n" ));
if ( g_McastStop )
{
DNSDBG( MCAST, ( "Terminating mcast loop.\n" ));
break;
}
//
// wait
//
pcontext = NULL;
bresult = GetQueuedCompletionStatus(
g_McastCompletionPort,
& bytesRecvd,
& (ULONG_PTR) pcontext,
& poverlapped,
INFINITE );
if ( g_McastStop )
{
DNSDBG( MCAST, (
"Terminating mcast loop after GQCS!\n" ));
break;
}
if ( pcontext )
{
pcontext->fRecvDown = FALSE;
}
if ( bresult )
{
if ( pcontext && bytesRecvd )
{
mcast_ProcessRecv( pcontext, bytesRecvd );
}
else
{
status = GetLastError();
DNSDBG( ANY, (
"Mcast GQCS() success without context!!!\n"
"\terror = %d\n",
status ));
Sleep( 100 );
continue;
}
}
else
{
status = GetLastError();
DNSDBG( ANY, (
"Mcast GQCS() failed %d\n"
"\terror = %d\n",
status ));
// Sleep( 100 );
continue;
}
}
Cleanup:
//
// cleanup multicast stuff
// - contexts, i/o completion port
// - note thread handle is closed by main thread as it is used
// for shutdown wait
//
mcast_CleanupIoContexts();
if ( g_McastCompletionPort )
{
CloseHandle( g_McastCompletionPort );
g_McastCompletionPort = 0;
}
//NetInfo_Free( g_pMcastNetinfo );
DNSDBG( TRACE, (
"Mcast thread %d exit!\n\n",
g_hMcastThread ));
g_hMcastThread = NULL;
}
//
// Public start\stop
//
DNS_STATUS
Mcast_Startup(
VOID
)
/*++
Routine Description:
Startup multicast listen.
Arguments:
None
Return Value:
None
--*/
{
DNS_STATUS status;
HANDLE hthread;
//
// screen
// - already started
// - no listen allowed
// - no IP4 listen and no IP6
//
if ( g_hMcastThread )
{
DNSDBG( ANY, (
"ERROR: called mcast listen with existing thread!\n" ));
return ERROR_ALREADY_EXISTS;
}
if ( g_MulticastListenLevel == MCAST_LISTEN_OFF )
{
DNSDBG( ANY, (
"No mcast listen -- mcast list disabled.\n" ));
return ERROR_NOT_SUPPORTED;
}
if ( ! (g_MulticastListenLevel & MCAST_LISTEN_IP4)
&&
! Util_IsIp6Running() )
{
DNSDBG( ANY, (
"No mcast listen -- no IP4 mcast listen.\n" ));
return ERROR_NOT_SUPPORTED;
}
//
// fire up IP notify thread
//
g_McastStop = FALSE;
status = NO_ERROR;
hthread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) Mcast_Thread,
NULL,
0,
& g_McastThreadId
);
if ( !hthread )
{
status = GetLastError();
DNSDBG( ANY, (
"FAILED to create IP notify thread = %d\n",
status ));
}
g_hMcastThread = hthread;
return status;
}
VOID
Mcast_SignalShutdown(
VOID
)
/*++
Routine Description:
Signal service shutdown to multicast thread.
Arguments:
None
Return Value:
None
--*/
{
g_McastStop = TRUE;
//
// shutdown recv
//
if ( g_McastCompletionPort )
{
PostQueuedCompletionStatus(
g_McastCompletionPort,
0,
0,
NULL );
}
}
VOID
Mcast_ShutdownWait(
VOID
)
/*++
Routine Description:
Wait for mcast shutdown.
This is for service stop routine.
Arguments:
None
Return Value:
None
--*/
{
HANDLE mcastThread = g_hMcastThread;
if ( ! mcastThread )
{
return;
}
//
// signal shutdown and wait
//
// note, local copy of thread handle, as mcast thread clears
// global when it exits to serve as flag
//
Mcast_SignalShutdown();
ThreadShutdownWait( mcastThread );
}
VOID
mcast_ProcessRecv(
IN OUT PMCSOCK_CONTEXT pContext,
IN DWORD BytesRecvd
)
/*++
Routine Description:
Process received packet.
Arguments:
pContext -- context for socket being recieved
BytesRecvd -- bytes received
Return Value:
None
--*/
{
PDNS_RECORD prr = NULL;
PDNS_HEADER phead;
CHAR nameQuestion[ DNS_MAX_NAME_BUFFER_LENGTH + 4 ];
WORD wtype;
WORD class;
PDNS_MSG_BUF pmsg;
PCHAR pnext;
WORD nameLength;
DNS_STATUS status;
//
// need new network info?
//
if ( g_McastConfigChange ||
! g_pMcastNetInfo )
{
NetInfo_Free( g_pMcastNetInfo );
DNSDBG( MCAST, ( "Mcast netinfo stale -- refreshing.\n" ));
g_pMcastNetInfo = GrabNetworkInfo();
if ( ! g_pMcastNetInfo )
{
DNSDBG( MCAST, ( "ERROR: failed to get netinfo for mcast!!!\n" ));
return;
}
}
//
// check packet, extract question info
//
// - flip header fields
// - opcode question
// - good format (Question==1, no Answer or Authority sections)
// - extract question
//
pmsg = pContext->pMsg;
if ( !pmsg )
{
ASSERT( FALSE );
return;
}
pmsg->MessageLength = (WORD)BytesRecvd;
phead = &pmsg->MessageHead;
DNS_BYTE_FLIP_HEADER_COUNTS( phead );
if ( phead->IsResponse ||
phead->Opcode != DNS_OPCODE_QUERY ||
phead->QuestionCount != 1 ||
phead->AnswerCount != 0 ||
phead->NameServerCount != 0 )
{
DNSDBG( ANY, (
"WARNING: invalid message recv'd by mcast listen!\n" ));
return;
}
// read question name
pnext = Dns_ReadPacketName(
nameQuestion,
& nameLength,
NULL, // no offset
NULL, // no previous name
(PCHAR) (phead + 1), // question name start
(PCHAR) phead, // message start
(PCHAR)phead + BytesRecvd // message end
);
if ( !pnext )
{
DNSDBG( ANY, (
"WARNING: invalid message question name recv'd by mcast!\n" ));
return;
}
// read question
wtype = InlineFlipUnalignedWord( pnext );
pnext += sizeof(WORD);
class = InlineFlipUnalignedWord( pnext );
pnext += sizeof(WORD);
if ( pnext < (PCHAR)phead+BytesRecvd ||
class != DNS_CLASS_INTERNET )
{
DNSDBG( ANY, (
"WARNING: invalid message question recv'd by mcast!\n" ));
return;
}
DNSDBG( MCAST, (
"Mcast recv msg (%p) qtype = %d, qname = %s\n",
pmsg,
wtype,
nameQuestion ));
//
// check query type
//
//
// for address\PTR types do local name check
//
// note: global mcast-netinfo valid, assumes this called only in single
// mcast response thread
//
// note: mcast query match no-data issue
// could have query match (name and no-ip of type) or (ip and
// unconfigured hostname) that could plausibly generate no-data
// response; not worrying about this as it doesn't add value;
// and hard to get (no-address -- how'd packet get here)
//
if ( wtype == DNS_TYPE_AAAA ||
wtype == DNS_TYPE_A ||
wtype == DNS_TYPE_PTR )
{
QUERY_BLOB blob;
WCHAR nameBuf[ DNS_MAX_NAME_BUFFER_LENGTH ];
if ( !Dns_NameCopyWireToUnicode(
nameBuf,
nameQuestion ) )
{
DNSDBG( ANY, (
"ERROR: invalid mcast name %s -- unable to convert to unicode!\n",
nameQuestion ));
return;
}
RtlZeroMemory( &blob, sizeof(blob) );
blob.pNameOrig = nameBuf;
blob.wType = wtype;
blob.Flags |= DNSP_QUERY_NO_GENERIC_NAMES;
blob.pNetInfo = g_pMcastNetInfo;
status = Local_GetRecordsForLocalName( &blob );
prr = blob.pRecords;
if ( status == NO_ERROR && prr )
{
goto Respond;
}
DNSDBG( MCAST, (
"Mcast name %S, no match against local data.!\n",
nameBuf ));
prr = blob.pRecords;
goto Cleanup;
}
//
// unsupported types
//
else
{
DNSDBG( MCAST, (
"WARNING: recv'd mcast type %d query -- currently unsupported.\n",
wtype ));
return;
}
Respond:
//
// write packet
// - records
// - set header bits
status = Dns_AddRecordsToMessage(
pmsg,
prr,
FALSE // not an update
);
if ( status != NO_ERROR )
{
DNSDBG( MCAST, (
"Failed mcast packet write!!!\n"
"\tstatus = %d\n",
status ));
goto Cleanup;
}
pmsg->MessageHead.IsResponse = TRUE;
pmsg->MessageHead.Authoritative = TRUE;
//
// send -- unicast back to client
//
// DCR: mcast OPT, could consider assuming all mcast
// are OPT aware (WinCE?)
//
Socket_ClearMessageSockets( pmsg );
status = Send_MessagePrivate(
pmsg,
& pmsg->RemoteAddress,
TRUE // no OPT
);
DNSDBG( MCAST, (
"Sent mcast response, status = %d\n",
status ));
Cleanup:
Dns_RecordListFree( prr );
return;
}
//
// End mcast.c
//