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

  1. /*++
  2. Copyright (c) 2001-2002 Microsoft Corporation
  3. Module Name:
  4. mcast.c
  5. Abstract:
  6. DNS Resolver Service
  7. Multicast routines.
  8. Author:
  9. James Gilroy (jamesg) December 2001
  10. Revision History:
  11. --*/
  12. #include "local.h"
  13. //
  14. // Socket context
  15. //
  16. typedef struct _McastSocketContext
  17. {
  18. struct _SocketContext * pNext;
  19. SOCKET Socket;
  20. PDNS_MSG_BUF pMsg;
  21. BOOL fRecvDown;
  22. OVERLAPPED Overlapped;
  23. }
  24. MCSOCK_CONTEXT, *PMCSOCK_CONTEXT;
  25. //
  26. // Globals
  27. //
  28. HANDLE g_hMcastThread = NULL;
  29. DWORD g_McastThreadId = 0;
  30. BOOL g_McastStop = FALSE;
  31. BOOL g_McastConfigChange = TRUE;
  32. HANDLE g_McastCompletionPort = NULL;
  33. PMCSOCK_CONTEXT g_pMcastContext4 = NULL;
  34. PMCSOCK_CONTEXT g_pMcastContext6 = NULL;
  35. PDNS_NETINFO g_pMcastNetInfo = NULL;
  36. PWSTR g_pwsMcastHostName = NULL;
  37. //
  38. // Mcast config globals
  39. //
  40. #define MCAST_RECORD_TTL 60 // 1 minute
  41. DWORD g_McastRecordTTL = MCAST_RECORD_TTL;
  42. //
  43. // Private prototypes
  44. //
  45. VOID
  46. mcast_ProcessRecv(
  47. IN OUT PMCSOCK_CONTEXT pContext,
  48. IN DWORD BytesRecvd
  49. );
  50. VOID
  51. mcast_FreeIoContext(
  52. IN OUT PMCSOCK_CONTEXT pContext
  53. )
  54. /*++
  55. Routine Description:
  56. Cleanup i/o context.
  57. Includes socket close.
  58. Arguments:
  59. pContext -- context for socket being recieved
  60. Return Value:
  61. None
  62. --*/
  63. {
  64. DNSDBG( TRACE, (
  65. "mcast_FreeIoContext( %p )\n",
  66. pContext ));
  67. //
  68. // cleanup context list
  69. // - close socket
  70. // - free message buffer
  71. // - free context
  72. //
  73. if ( pContext )
  74. {
  75. Socket_Close( pContext->Socket );
  76. MCAST_HEAP_FREE( pContext->pMsg );
  77. MCAST_HEAP_FREE( pContext );
  78. }
  79. }
  80. PMCSOCK_CONTEXT
  81. mcast_CreateIoContext(
  82. IN INT Family,
  83. IN PWSTR pName
  84. )
  85. /*++
  86. Routine Description:
  87. Create and init a i/o context for a protocol.
  88. Arguments:
  89. Family -- address family
  90. pName -- name to be published
  91. Return Value:
  92. NO_ERROR if successful.
  93. ErrorCode on failure.
  94. --*/
  95. {
  96. DNS_STATUS status = NO_ERROR;
  97. PMCSOCK_CONTEXT pcontext = NULL;
  98. SOCKET sock = 0;
  99. DNS_ADDR addr;
  100. HANDLE hport;
  101. //
  102. // setup mcast address
  103. //
  104. status = DnsAddr_BuildMcast(
  105. &addr,
  106. Family,
  107. pName
  108. );
  109. if ( status != NO_ERROR )
  110. {
  111. goto Failed;
  112. }
  113. //
  114. // create multicast socket
  115. // - bound to this address and DNS port
  116. //
  117. sock = Socket_CreateMulticast(
  118. SOCK_DGRAM,
  119. &addr,
  120. MCAST_PORT_NET_ORDER,
  121. FALSE, // not send
  122. TRUE // receive
  123. );
  124. if ( sock == 0 )
  125. {
  126. goto Failed;
  127. }
  128. //
  129. // DCR: mcast context could include message buffer
  130. // (or even just reassign some fields in context
  131. //
  132. // allocate and clear context
  133. pcontext = MCAST_HEAP_ALLOC_ZERO( sizeof(MCSOCK_CONTEXT) );
  134. if ( !pcontext )
  135. {
  136. goto Failed;
  137. }
  138. // create message buffer for socket
  139. pcontext->pMsg = Dns_AllocateMsgBuf( 0 );
  140. if ( !pcontext->pMsg )
  141. {
  142. DNSDBG( ANY, ( "Error: Failed to allocate message buffer." ));
  143. goto Failed;
  144. }
  145. // attach to completion port
  146. hport = CreateIoCompletionPort(
  147. (HANDLE) sock,
  148. g_McastCompletionPort,
  149. (UINT_PTR) pcontext,
  150. 0 );
  151. if ( !hport )
  152. {
  153. DNSDBG( INIT, (
  154. "Failed to add socket to io completion port." ));
  155. goto Failed;
  156. }
  157. // fill in context
  158. pcontext->Socket = sock;
  159. sock = 0;
  160. pcontext->pMsg->fTcp = FALSE;
  161. #if 0
  162. // not handling contexts in list yet
  163. // insert into list
  164. InsertTailList( (PLIST_ENTRY)pcontext );
  165. #endif
  166. return pcontext;
  167. Failed:
  168. Socket_Close( sock );
  169. mcast_FreeIoContext( pcontext );
  170. return NULL;
  171. }
  172. VOID
  173. mcast_DropReceive(
  174. IN OUT PMCSOCK_CONTEXT pContext
  175. )
  176. /*++
  177. Routine Description:
  178. Drop down UDP receive request.
  179. Arguments:
  180. pContext -- context for socket being recieved
  181. Return Value:
  182. None
  183. --*/
  184. {
  185. DNS_STATUS status;
  186. WSABUF wsaBuf;
  187. DWORD bytesRecvd;
  188. DWORD flags = 0;
  189. INT retry = 0;
  190. DNSDBG( TRACE, (
  191. "mcast_DropReceive( %p )\n",
  192. pContext ));
  193. if ( !pContext || !pContext->pMsg )
  194. {
  195. DNS_ASSERT( FALSE );
  196. return;
  197. }
  198. //
  199. // DCR: could support larger MCAST packets
  200. //
  201. wsaBuf.len = DNS_MAX_UDP_PACKET_BUFFER_LENGTH;
  202. wsaBuf.buf = (PCHAR) (&pContext->pMsg->MessageHead);
  203. pContext->pMsg->Socket = pContext->Socket;
  204. //
  205. // loop until successful WSARecvFrom() is down
  206. //
  207. // this loop is only active while we continue to recv
  208. // WSAECONNRESET or WSAEMSGSIZE errors, both of which
  209. // cause us to dump data and retry;
  210. //
  211. // note loop rather than recursion (to this function) is
  212. // required to avoid possible stack overflow from malicious
  213. // send
  214. //
  215. // normal returns from WSARecvFrom() are
  216. // SUCCESS -- packet was waiting, GQCS will fire immediately
  217. // WSA_IO_PENDING -- no data yet, GQCS will fire when ready
  218. //
  219. while ( 1 )
  220. {
  221. retry++;
  222. status = WSARecvFrom(
  223. pContext->Socket,
  224. & wsaBuf,
  225. 1,
  226. & bytesRecvd,
  227. & flags,
  228. & pContext->pMsg->RemoteAddress.Sockaddr,
  229. & pContext->pMsg->RemoteAddress.SockaddrLength,
  230. & pContext->Overlapped,
  231. NULL );
  232. if ( status == ERROR_SUCCESS )
  233. {
  234. pContext->fRecvDown = TRUE;
  235. return;
  236. }
  237. status = GetLastError();
  238. if ( status == WSA_IO_PENDING )
  239. {
  240. pContext->fRecvDown = TRUE;
  241. return;
  242. }
  243. //
  244. // when last send ICMP'd
  245. // - set flag to indicate retry and repost send
  246. // - if over some reasonable number of retries, assume error
  247. // and fall through recv failure code
  248. //
  249. if ( status == WSAECONNRESET ||
  250. status == WSAEMSGSIZE )
  251. {
  252. if ( retry > 10 )
  253. {
  254. Sleep( retry );
  255. }
  256. continue;
  257. }
  258. return;
  259. }
  260. }
  261. VOID
  262. mcast_CreateIoContexts(
  263. VOID
  264. )
  265. /*++
  266. Routine Description:
  267. Create any uncreated i/o contexts.
  268. This is run dynamically -- after every mcast recv -- and creates
  269. any missing contexts, hence handling either previous failure or
  270. change in config (name change, protocol change).
  271. Arguments:
  272. None
  273. Return Value:
  274. None
  275. --*/
  276. {
  277. PDNS_NETINFO pnetInfo;
  278. //
  279. // get current netinfo
  280. //
  281. // DCR: should have "do we need new" netinfo check
  282. //
  283. pnetInfo = GrabNetworkInfo();
  284. if ( pnetInfo )
  285. {
  286. NetInfo_Free( g_pMcastNetInfo );
  287. g_pMcastNetInfo = pnetInfo;
  288. }
  289. else
  290. {
  291. pnetInfo = g_pMcastNetInfo;
  292. }
  293. //
  294. // try\retry context setup, to handle dynamic IP\name
  295. //
  296. if ( !g_pMcastContext6 ||
  297. !Dns_NameCompare_W(
  298. g_pwsMcastHostName,
  299. pnetInfo->pszHostName ) )
  300. {
  301. g_pMcastContext6 = mcast_CreateIoContext( AF_INET6, pnetInfo->pszHostName );
  302. Dns_Free( g_pwsMcastHostName );
  303. g_pwsMcastHostName = Dns_CreateStringCopy_W( pnetInfo->pszHostName );
  304. }
  305. if ( !g_pMcastContext4 )
  306. {
  307. g_pMcastContext4 = mcast_CreateIoContext( AF_INET, NULL );
  308. }
  309. //
  310. // redrop receives if not outstanding
  311. //
  312. mcast_DropReceive( g_pMcastContext6 );
  313. mcast_DropReceive( g_pMcastContext4 );
  314. }
  315. VOID
  316. mcast_CleanupIoContexts(
  317. VOID
  318. )
  319. /*++
  320. Routine Description:
  321. Cleanup mcast i/o contexts created.
  322. Arguments:
  323. None
  324. Return Value:
  325. None
  326. --*/
  327. {
  328. mcast_FreeIoContext( g_pMcastContext6 );
  329. mcast_FreeIoContext( g_pMcastContext4 );
  330. g_pMcastContext6 = NULL;
  331. g_pMcastContext4 = NULL;
  332. }
  333. VOID
  334. Mcast_Thread(
  335. VOID
  336. )
  337. /*++
  338. Routine Description:
  339. Mcast response thread.
  340. Runs while cache is running, responding to multicast queries.
  341. Arguments:
  342. None.
  343. Return Value:
  344. None.
  345. --*/
  346. {
  347. DNS_STATUS status = NO_ERROR;
  348. PMCSOCK_CONTEXT pcontext;
  349. DWORD bytesRecvd;
  350. LPOVERLAPPED poverlapped;
  351. BOOL bresult;
  352. DNSDBG( MCAST, ( "Mcast_Thread() start!\n" ));
  353. //
  354. // init mcast globals
  355. //
  356. g_McastStop = FALSE;
  357. g_McastCompletionPort = NULL;
  358. //
  359. // create mcast completion port
  360. //
  361. g_McastCompletionPort = CreateIoCompletionPort(
  362. INVALID_HANDLE_VALUE,
  363. NULL,
  364. 0,
  365. 0 );
  366. if ( ! g_McastCompletionPort )
  367. {
  368. DNSDBG( ANY, (
  369. "Error: Failed to create mcast completion port.\n" ));
  370. goto Cleanup;
  371. }
  372. //
  373. // main listen loop
  374. //
  375. while ( 1 )
  376. {
  377. DNSDBG( MCAST, ( "Top of loop!\n" ));
  378. if ( g_McastStop )
  379. {
  380. DNSDBG( MCAST, ( "Terminating mcast loop.\n" ));
  381. break;
  382. }
  383. //
  384. // wait
  385. //
  386. pcontext = NULL;
  387. bresult = GetQueuedCompletionStatus(
  388. g_McastCompletionPort,
  389. & bytesRecvd,
  390. & (ULONG_PTR) pcontext,
  391. & poverlapped,
  392. INFINITE );
  393. if ( g_McastStop )
  394. {
  395. DNSDBG( MCAST, (
  396. "Terminating mcast loop after GQCS!\n" ));
  397. break;
  398. }
  399. if ( pcontext )
  400. {
  401. pcontext->fRecvDown = FALSE;
  402. }
  403. if ( bresult )
  404. {
  405. if ( pcontext && bytesRecvd )
  406. {
  407. mcast_ProcessRecv( pcontext, bytesRecvd );
  408. }
  409. else
  410. {
  411. status = GetLastError();
  412. DNSDBG( ANY, (
  413. "Mcast GQCS() success without context!!!\n"
  414. "\terror = %d\n",
  415. status ));
  416. Sleep( 100 );
  417. continue;
  418. }
  419. }
  420. else
  421. {
  422. status = GetLastError();
  423. DNSDBG( ANY, (
  424. "Mcast GQCS() failed %d\n"
  425. "\terror = %d\n",
  426. status ));
  427. // Sleep( 100 );
  428. continue;
  429. }
  430. }
  431. Cleanup:
  432. //
  433. // cleanup multicast stuff
  434. // - contexts, i/o completion port
  435. // - note thread handle is closed by main thread as it is used
  436. // for shutdown wait
  437. //
  438. mcast_CleanupIoContexts();
  439. if ( g_McastCompletionPort )
  440. {
  441. CloseHandle( g_McastCompletionPort );
  442. g_McastCompletionPort = 0;
  443. }
  444. //NetInfo_Free( g_pMcastNetinfo );
  445. DNSDBG( TRACE, (
  446. "Mcast thread %d exit!\n\n",
  447. g_hMcastThread ));
  448. g_hMcastThread = NULL;
  449. }
  450. //
  451. // Public start\stop
  452. //
  453. DNS_STATUS
  454. Mcast_Startup(
  455. VOID
  456. )
  457. /*++
  458. Routine Description:
  459. Startup multicast listen.
  460. Arguments:
  461. None
  462. Return Value:
  463. None
  464. --*/
  465. {
  466. DNS_STATUS status;
  467. HANDLE hthread;
  468. //
  469. // screen
  470. // - already started
  471. // - no listen allowed
  472. // - no IP4 listen and no IP6
  473. //
  474. if ( g_hMcastThread )
  475. {
  476. DNSDBG( ANY, (
  477. "ERROR: called mcast listen with existing thread!\n" ));
  478. return ERROR_ALREADY_EXISTS;
  479. }
  480. if ( g_MulticastListenLevel == MCAST_LISTEN_OFF )
  481. {
  482. DNSDBG( ANY, (
  483. "No mcast listen -- mcast list disabled.\n" ));
  484. return ERROR_NOT_SUPPORTED;
  485. }
  486. if ( ! (g_MulticastListenLevel & MCAST_LISTEN_IP4)
  487. &&
  488. ! Util_IsIp6Running() )
  489. {
  490. DNSDBG( ANY, (
  491. "No mcast listen -- no IP4 mcast listen.\n" ));
  492. return ERROR_NOT_SUPPORTED;
  493. }
  494. //
  495. // fire up IP notify thread
  496. //
  497. g_McastStop = FALSE;
  498. status = NO_ERROR;
  499. hthread = CreateThread(
  500. NULL,
  501. 0,
  502. (LPTHREAD_START_ROUTINE) Mcast_Thread,
  503. NULL,
  504. 0,
  505. & g_McastThreadId
  506. );
  507. if ( !hthread )
  508. {
  509. status = GetLastError();
  510. DNSDBG( ANY, (
  511. "FAILED to create IP notify thread = %d\n",
  512. status ));
  513. }
  514. g_hMcastThread = hthread;
  515. return status;
  516. }
  517. VOID
  518. Mcast_SignalShutdown(
  519. VOID
  520. )
  521. /*++
  522. Routine Description:
  523. Signal service shutdown to multicast thread.
  524. Arguments:
  525. None
  526. Return Value:
  527. None
  528. --*/
  529. {
  530. g_McastStop = TRUE;
  531. //
  532. // shutdown recv
  533. //
  534. if ( g_McastCompletionPort )
  535. {
  536. PostQueuedCompletionStatus(
  537. g_McastCompletionPort,
  538. 0,
  539. 0,
  540. NULL );
  541. }
  542. }
  543. VOID
  544. Mcast_ShutdownWait(
  545. VOID
  546. )
  547. /*++
  548. Routine Description:
  549. Wait for mcast shutdown.
  550. This is for service stop routine.
  551. Arguments:
  552. None
  553. Return Value:
  554. None
  555. --*/
  556. {
  557. HANDLE mcastThread = g_hMcastThread;
  558. if ( ! mcastThread )
  559. {
  560. return;
  561. }
  562. //
  563. // signal shutdown and wait
  564. //
  565. // note, local copy of thread handle, as mcast thread clears
  566. // global when it exits to serve as flag
  567. //
  568. Mcast_SignalShutdown();
  569. ThreadShutdownWait( mcastThread );
  570. }
  571. VOID
  572. mcast_ProcessRecv(
  573. IN OUT PMCSOCK_CONTEXT pContext,
  574. IN DWORD BytesRecvd
  575. )
  576. /*++
  577. Routine Description:
  578. Process received packet.
  579. Arguments:
  580. pContext -- context for socket being recieved
  581. BytesRecvd -- bytes received
  582. Return Value:
  583. None
  584. --*/
  585. {
  586. PDNS_RECORD prr = NULL;
  587. PDNS_HEADER phead;
  588. CHAR nameQuestion[ DNS_MAX_NAME_BUFFER_LENGTH + 4 ];
  589. WORD wtype;
  590. WORD class;
  591. PDNS_MSG_BUF pmsg;
  592. PCHAR pnext;
  593. WORD nameLength;
  594. DNS_STATUS status;
  595. //
  596. // need new network info?
  597. //
  598. if ( g_McastConfigChange ||
  599. ! g_pMcastNetInfo )
  600. {
  601. NetInfo_Free( g_pMcastNetInfo );
  602. DNSDBG( MCAST, ( "Mcast netinfo stale -- refreshing.\n" ));
  603. g_pMcastNetInfo = GrabNetworkInfo();
  604. if ( ! g_pMcastNetInfo )
  605. {
  606. DNSDBG( MCAST, ( "ERROR: failed to get netinfo for mcast!!!\n" ));
  607. return;
  608. }
  609. }
  610. //
  611. // check packet, extract question info
  612. //
  613. // - flip header fields
  614. // - opcode question
  615. // - good format (Question==1, no Answer or Authority sections)
  616. // - extract question
  617. //
  618. pmsg = pContext->pMsg;
  619. if ( !pmsg )
  620. {
  621. ASSERT( FALSE );
  622. return;
  623. }
  624. pmsg->MessageLength = (WORD)BytesRecvd;
  625. phead = &pmsg->MessageHead;
  626. DNS_BYTE_FLIP_HEADER_COUNTS( phead );
  627. if ( phead->IsResponse ||
  628. phead->Opcode != DNS_OPCODE_QUERY ||
  629. phead->QuestionCount != 1 ||
  630. phead->AnswerCount != 0 ||
  631. phead->NameServerCount != 0 )
  632. {
  633. DNSDBG( ANY, (
  634. "WARNING: invalid message recv'd by mcast listen!\n" ));
  635. return;
  636. }
  637. // read question name
  638. pnext = Dns_ReadPacketName(
  639. nameQuestion,
  640. & nameLength,
  641. NULL, // no offset
  642. NULL, // no previous name
  643. (PCHAR) (phead + 1), // question name start
  644. (PCHAR) phead, // message start
  645. (PCHAR)phead + BytesRecvd // message end
  646. );
  647. if ( !pnext )
  648. {
  649. DNSDBG( ANY, (
  650. "WARNING: invalid message question name recv'd by mcast!\n" ));
  651. return;
  652. }
  653. // read question
  654. wtype = InlineFlipUnalignedWord( pnext );
  655. pnext += sizeof(WORD);
  656. class = InlineFlipUnalignedWord( pnext );
  657. pnext += sizeof(WORD);
  658. if ( pnext < (PCHAR)phead+BytesRecvd ||
  659. class != DNS_CLASS_INTERNET )
  660. {
  661. DNSDBG( ANY, (
  662. "WARNING: invalid message question recv'd by mcast!\n" ));
  663. return;
  664. }
  665. DNSDBG( MCAST, (
  666. "Mcast recv msg (%p) qtype = %d, qname = %s\n",
  667. pmsg,
  668. wtype,
  669. nameQuestion ));
  670. //
  671. // check query type
  672. //
  673. //
  674. // for address\PTR types do local name check
  675. //
  676. // note: global mcast-netinfo valid, assumes this called only in single
  677. // mcast response thread
  678. //
  679. // note: mcast query match no-data issue
  680. // could have query match (name and no-ip of type) or (ip and
  681. // unconfigured hostname) that could plausibly generate no-data
  682. // response; not worrying about this as it doesn't add value;
  683. // and hard to get (no-address -- how'd packet get here)
  684. //
  685. if ( wtype == DNS_TYPE_AAAA ||
  686. wtype == DNS_TYPE_A ||
  687. wtype == DNS_TYPE_PTR )
  688. {
  689. QUERY_BLOB blob;
  690. WCHAR nameBuf[ DNS_MAX_NAME_BUFFER_LENGTH ];
  691. if ( !Dns_NameCopyWireToUnicode(
  692. nameBuf,
  693. nameQuestion ) )
  694. {
  695. DNSDBG( ANY, (
  696. "ERROR: invalid mcast name %s -- unable to convert to unicode!\n",
  697. nameQuestion ));
  698. return;
  699. }
  700. RtlZeroMemory( &blob, sizeof(blob) );
  701. blob.pNameOrig = nameBuf;
  702. blob.wType = wtype;
  703. blob.Flags |= DNSP_QUERY_NO_GENERIC_NAMES;
  704. blob.pNetInfo = g_pMcastNetInfo;
  705. status = Local_GetRecordsForLocalName( &blob );
  706. prr = blob.pRecords;
  707. if ( status == NO_ERROR && prr )
  708. {
  709. goto Respond;
  710. }
  711. DNSDBG( MCAST, (
  712. "Mcast name %S, no match against local data.!\n",
  713. nameBuf ));
  714. prr = blob.pRecords;
  715. goto Cleanup;
  716. }
  717. //
  718. // unsupported types
  719. //
  720. else
  721. {
  722. DNSDBG( MCAST, (
  723. "WARNING: recv'd mcast type %d query -- currently unsupported.\n",
  724. wtype ));
  725. return;
  726. }
  727. Respond:
  728. //
  729. // write packet
  730. // - records
  731. // - set header bits
  732. status = Dns_AddRecordsToMessage(
  733. pmsg,
  734. prr,
  735. FALSE // not an update
  736. );
  737. if ( status != NO_ERROR )
  738. {
  739. DNSDBG( MCAST, (
  740. "Failed mcast packet write!!!\n"
  741. "\tstatus = %d\n",
  742. status ));
  743. goto Cleanup;
  744. }
  745. pmsg->MessageHead.IsResponse = TRUE;
  746. pmsg->MessageHead.Authoritative = TRUE;
  747. //
  748. // send -- unicast back to client
  749. //
  750. // DCR: mcast OPT, could consider assuming all mcast
  751. // are OPT aware (WinCE?)
  752. //
  753. Socket_ClearMessageSockets( pmsg );
  754. status = Send_MessagePrivate(
  755. pmsg,
  756. & pmsg->RemoteAddress,
  757. TRUE // no OPT
  758. );
  759. DNSDBG( MCAST, (
  760. "Sent mcast response, status = %d\n",
  761. status ));
  762. Cleanup:
  763. Dns_RecordListFree( prr );
  764. return;
  765. }
  766. //
  767. // End mcast.c
  768. //