Source code of Windows XP (NT5)
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.

3554 lines
79 KiB

  1. /*++
  2. Copyright (c) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. packet.c
  5. Abstract:
  6. Domain Name System (DNS) API
  7. Packet writing utilities.
  8. Author:
  9. Jim Gilroy (jamesg) October, 1996
  10. Environment:
  11. User Mode - Win32
  12. Revision History:
  13. --*/
  14. #include "local.h"
  15. //
  16. // Global XID counter, to generate unique XIDs
  17. //
  18. WORD gwTransactionId = 1;
  19. //
  20. // Receive buffer size
  21. // - use 16K, max size where compression useful
  22. // other choices would be ethernet UDP frag size
  23. // (1472 or 1280 depending on who you talk to)
  24. //
  25. DWORD g_RecvBufSize = 0x4000;
  26. //
  27. // Class values for UPDATE packets
  28. // (Key concept here -- designed by committee)
  29. //
  30. // These arrays are indexed by
  31. // !wDataLength -- row
  32. // Delete flag -- column
  33. //
  34. WORD PrereqClassArray[2][2] =
  35. {
  36. DNS_RCLASS_INTERNET, // data != 0, no delete
  37. 0, // data != 0, delete => ERROR
  38. DNS_RCLASS_ANY, // no data, no delete
  39. DNS_RCLASS_NONE, // no data, delete
  40. };
  41. WORD UpdateClassArray[2][2] =
  42. {
  43. DNS_RCLASS_INTERNET, // data != 0, no delete
  44. DNS_RCLASS_NONE, // data != 0, delete
  45. 0, // no data, no delete => ERROR
  46. DNS_RCLASS_ANY, // no data, delete
  47. };
  48. PDNS_MSG_BUF
  49. Dns_AllocateMsgBuf(
  50. IN WORD wBufferLength OPTIONAL
  51. )
  52. /*++
  53. Routine Description:
  54. Allocate message buffer.
  55. Arguments:
  56. wBufferLength - optional length of message buffer; default is MAX
  57. UDP size
  58. Return Value:
  59. Ptr to message buffer.
  60. NULL on error.
  61. --*/
  62. {
  63. PDNS_MSG_BUF pmsg;
  64. BOOL ftcp = FALSE;
  65. WORD allocLength;
  66. //
  67. // default allocation to "classical" UDP max buffer length
  68. // this is good enough for writing questions
  69. // recv size buffers should request larger size
  70. //
  71. if ( wBufferLength < DNS_RFC_MAX_UDP_PACKET_LENGTH )
  72. {
  73. allocLength = DNS_RFC_MAX_UDP_PACKET_LENGTH;
  74. }
  75. else if ( wBufferLength == MAXWORD )
  76. {
  77. allocLength = (WORD) g_RecvBufSize;
  78. }
  79. else
  80. {
  81. allocLength = wBufferLength;
  82. ftcp = TRUE;
  83. }
  84. pmsg = ALLOCATE_HEAP( SIZEOF_MSG_BUF_OVERHEAD + allocLength );
  85. if ( !pmsg )
  86. {
  87. return( NULL );
  88. }
  89. //
  90. // limit UDP sends to classical RFC UDP limit (512 bytes)
  91. // regardless of actual allocation
  92. // write routines use pBufferEnd to determine writeability
  93. //
  94. // DCR: allow big UDP send buffers for update?
  95. // problem is that must roll back NOT just OPT, but also
  96. // big buffer
  97. //
  98. // DCR: not really necessary as if write exceeds 512, can
  99. // just flip to TCP anyway
  100. //
  101. pmsg->BufferLength = allocLength;
  102. if ( !ftcp )
  103. {
  104. allocLength = DNS_RFC_MAX_UDP_PACKET_LENGTH;
  105. }
  106. pmsg->pBufferEnd = (PCHAR)&pmsg->MessageHead + allocLength;
  107. pmsg->fTcp = (BOOLEAN)ftcp;
  108. //
  109. // init -- this follows fTcp set as flag is used to set fields
  110. //
  111. Dns_InitializeMsgBuf( pmsg );
  112. return( pmsg );
  113. }
  114. VOID
  115. Dns_InitializeMsgBuf(
  116. IN OUT PDNS_MSG_BUF pMsg
  117. )
  118. /*++
  119. Routine Description:
  120. Initialize message buffer to "clean" state.
  121. Arguments:
  122. pMsg -- message to init
  123. Return Value:
  124. Ptr to message buffer.
  125. NULL on error.
  126. --*/
  127. {
  128. // clear info + header
  129. //
  130. // DCR_CLEANUP: can't take this approach without reworking Allocate
  131. // function which sets BufferLength and pBufferEnd
  132. // if this function is NOT independently used, then we can fix
  133. // it to clean and completely
  134. //
  135. // RtlZeroMemory(
  136. // pMsg,
  137. // ((PBYTE)&pMsg->MessageBody - (PBYTE)pMsg) );
  138. // setup addressing info
  139. pMsg->Socket = 0;
  140. pMsg->RemoteAddressLength = sizeof(SOCKADDR_IN);
  141. // set for packet reception
  142. if ( pMsg->fTcp )
  143. {
  144. SET_MESSAGE_FOR_TCP_RECV( pMsg );
  145. }
  146. else
  147. {
  148. SET_MESSAGE_FOR_UDP_RECV( pMsg );
  149. }
  150. // clear header
  151. RtlZeroMemory(
  152. (PBYTE) &pMsg->MessageBody,
  153. sizeof(DNS_HEADER) );
  154. // set for rewriting
  155. pMsg->pCurrent = pMsg->MessageBody;
  156. pMsg->pPreOptEnd = NULL;
  157. }
  158. //
  159. // Writing to packet
  160. //
  161. PCHAR
  162. _fastcall
  163. Dns_WriteDottedNameToPacket(
  164. IN OUT PCHAR pch,
  165. IN PCHAR pchStop,
  166. IN LPSTR pszName,
  167. IN LPSTR pszDomain, OPTIONAL
  168. IN WORD wDomainOffset, OPTIONAL
  169. IN BOOL fUnicodeName
  170. )
  171. /*++
  172. Routine Description:
  173. Write lookup name to packet.
  174. Arguments:
  175. pch -- ptr to current position in packet buffer
  176. pchStop -- end of packet buffer
  177. pszName - dotted FQDN to write
  178. pszDomain - domain name already in packet (OPTIONAL); note this is
  179. a fragment of the SAME STRING as pszName; i.e. ptr compare
  180. NOT strcmp is done to determine if at domain name
  181. wDomainOffset - offset in packet of domain name; MUST include this
  182. if pszDomain is given
  183. fUnicodeName -- pszName is unicode string
  184. TRUE -- name is unicode
  185. FALSE -- name is UTF8
  186. Return Value:
  187. Ptr to next position in packet buffer.
  188. NULL on error.
  189. --*/
  190. {
  191. CHAR ch;
  192. PCHAR pchlabelStart;
  193. UCHAR countLabel = 0;
  194. ULONG countName = 0;
  195. PSTR pnameWire;
  196. PWSTR pnameUnicode;
  197. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  198. WCHAR nameWideBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  199. // protect message buffer overrun
  200. if ( pch >= pchStop )
  201. {
  202. return( NULL );
  203. }
  204. // allow root to be indicated by NULL
  205. if ( !pszName )
  206. {
  207. *pch++ = 0;
  208. return( pch );
  209. }
  210. //
  211. // protect stack buffers from bogus names
  212. //
  213. if ( fUnicodeName )
  214. {
  215. countName = wcslen( (LPWSTR) pszName );
  216. }
  217. else
  218. {
  219. countName = strlen( pszName );
  220. }
  221. if ( countName >= DNS_MAX_NAME_BUFFER_LENGTH )
  222. {
  223. return NULL;
  224. }
  225. countName = 0;
  226. //
  227. // UTF8 name with extended chars?
  228. // - then must go up to unicode for canonicalizing
  229. //
  230. // DCR: shouldn't be sending in un-canonical UTF8
  231. // should
  232. // - stay in unicode all the way
  233. // - using canon unicode all the way
  234. // - use canon wire names all the way
  235. //
  236. if ( !fUnicodeName )
  237. {
  238. if ( !Dns_IsStringAscii( pszName ) )
  239. {
  240. DWORD bufLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  241. if ( ! Dns_NameCopy(
  242. (PCHAR) nameWideBuffer,
  243. & bufLength,
  244. pszName,
  245. 0, // length unknown
  246. DnsCharSetUtf8,
  247. DnsCharSetUnicode
  248. ) )
  249. {
  250. return( NULL );
  251. }
  252. if ( ! Dns_MakeCanonicalNameInPlaceW(
  253. nameWideBuffer,
  254. 0 ) )
  255. {
  256. return( NULL );
  257. }
  258. pnameUnicode = (PWSTR) nameWideBuffer;
  259. fUnicodeName = TRUE;
  260. }
  261. }
  262. //
  263. // unicode name -- if extended, canonicalize first
  264. //
  265. // DCR_FIX0: should functionalize -- raw unicode to wire
  266. //
  267. else
  268. {
  269. pnameUnicode = (PWSTR) pszName;
  270. if ( !Dns_IsWideStringAscii( pnameUnicode ) )
  271. {
  272. if ( ! Dns_MakeCanonicalNameW(
  273. nameWideBuffer,
  274. //DNS_MAX_NAME_BUFFER_LENGTH_UNICODE,
  275. DNS_MAX_NAME_BUFFER_LENGTH,
  276. pnameUnicode,
  277. 0 ) )
  278. {
  279. return NULL;
  280. }
  281. pnameUnicode = nameWideBuffer;
  282. }
  283. }
  284. //
  285. // convert unicode name to UTF8
  286. // - if extended chars, then downcase before hit the wire
  287. //
  288. if ( fUnicodeName )
  289. {
  290. if ( ! Dns_NameCopyUnicodeToWire(
  291. nameBuffer,
  292. pnameUnicode ) )
  293. {
  294. return( NULL );
  295. }
  296. pnameWire = nameBuffer;
  297. }
  298. else
  299. {
  300. pnameWire = pszName;
  301. }
  302. //
  303. // special case "." root name
  304. // - allows us to fail all other zero length labels cleanly
  305. //
  306. if ( *pnameWire == '.' )
  307. {
  308. if ( *(pnameWire+1) != 0 )
  309. {
  310. return( NULL );
  311. }
  312. *pch++ = 0;
  313. return( pch );
  314. }
  315. //
  316. // write lookup name
  317. // - leave
  318. pchlabelStart = pch++;
  319. while( ch = *pnameWire++ )
  320. {
  321. // out of space
  322. if ( pch >= pchStop )
  323. {
  324. return( NULL );
  325. }
  326. // not at end of label -- just copy character
  327. if ( ch != '.' )
  328. {
  329. *pch++ = ch;
  330. countLabel++;
  331. countName++;
  332. continue;
  333. }
  334. //
  335. // at end of label
  336. // - write label count
  337. // - reset counter
  338. // - if reached domain name, write compression and quit
  339. //
  340. if ( countLabel > DNS_MAX_LABEL_LENGTH ||
  341. countLabel == 0 ||
  342. countName > DNS_MAX_NAME_LENGTH )
  343. {
  344. return( NULL );
  345. }
  346. *pchlabelStart = countLabel;
  347. countLabel = 0;
  348. countName++;
  349. pchlabelStart = pch++;
  350. if ( pnameWire == pszDomain )
  351. {
  352. if ( ++pch >= pchStop )
  353. {
  354. return( NULL );
  355. }
  356. *(UNALIGNED WORD *)pchlabelStart =
  357. htons( (WORD)(wDomainOffset | (WORD)0xC000) );
  358. return( pch );
  359. }
  360. }
  361. if ( countLabel > DNS_MAX_LABEL_LENGTH ||
  362. countName > DNS_MAX_NAME_LENGTH )
  363. {
  364. return( NULL );
  365. }
  366. //
  367. // NULL terminate
  368. // - if no terminating ".", do end of label processing also
  369. // - return ptr to char after terminating NULL
  370. if ( countLabel )
  371. {
  372. *pchlabelStart = countLabel;
  373. *pch++ = 0;
  374. }
  375. else
  376. {
  377. DNS_ASSERT( pch == pchlabelStart + 1 );
  378. *pchlabelStart = 0;
  379. }
  380. return( pch );
  381. }
  382. PCHAR
  383. _fastcall
  384. Dns_WriteStringToPacket(
  385. IN OUT PCHAR pch,
  386. IN PCHAR pchStop,
  387. IN PSTR pszString,
  388. IN BOOL fUnicodeString
  389. )
  390. /*++
  391. Routine Description:
  392. Write string to packet.
  393. Arguments:
  394. pch -- ptr to current position in packet buffer
  395. pchStop -- end of packet buffer
  396. pszString - string to write
  397. fUnicodeString -- pszString is unicode string
  398. Return Value:
  399. Ptr to next position in packet buffer.
  400. NULL on error.
  401. --*/
  402. {
  403. DWORD length;
  404. //
  405. // handle NULL string
  406. //
  407. if ( !pszString )
  408. {
  409. if ( pch >= pchStop )
  410. {
  411. return( NULL );
  412. }
  413. *pch++ = 0;
  414. return( pch );
  415. }
  416. //
  417. // get string length
  418. // - get required buf length, then whack whack off space
  419. // for terminating NULL
  420. // - zero error case, becomes very large number and is
  421. // caught by length>MAXCHAR test
  422. //
  423. length = Dns_GetBufferLengthForStringCopy(
  424. pszString,
  425. 0,
  426. fUnicodeString ? DnsCharSetUnicode : DnsCharSetUtf8,
  427. DnsCharSetUtf8 );
  428. length--;
  429. //
  430. // set length byte
  431. //
  432. if ( length > MAXUCHAR )
  433. {
  434. SetLastError( ERROR_INVALID_DATA );
  435. return( NULL );
  436. }
  437. *pch++ = (UCHAR) length;
  438. if ( pch + length > pchStop )
  439. {
  440. SetLastError( ERROR_MORE_DATA );
  441. return( NULL );
  442. }
  443. //
  444. // copy string
  445. //
  446. // note unicode conversion will write NULL terminator, so
  447. // test again for space in packet
  448. //
  449. if ( fUnicodeString )
  450. {
  451. if ( pch + length + 1 > pchStop )
  452. {
  453. SetLastError( ERROR_MORE_DATA );
  454. return( NULL );
  455. }
  456. Dns_StringCopy(
  457. pch,
  458. NULL,
  459. pszString,
  460. length,
  461. DnsCharSetUnicode,
  462. DnsCharSetUtf8 );
  463. }
  464. else
  465. {
  466. memcpy(
  467. pch,
  468. pszString,
  469. length );
  470. }
  471. return( pch+length );
  472. }
  473. PCHAR
  474. Dns_WriteQuestionToMessage(
  475. IN OUT PDNS_MSG_BUF pMsg,
  476. IN PDNS_NAME pszName,
  477. IN WORD wType,
  478. IN BOOL fUnicodeName
  479. )
  480. /*++
  481. Routine Description:
  482. Write question to packet.
  483. Note: Routine DOES NOT clear message info or message header.
  484. This is optimized for use immediately following Dns_CreateMessage().
  485. Arguments:
  486. pMsg - message info
  487. pszName - DNS name of question
  488. wType - question type
  489. fUnicodeName - name is in unicode
  490. Return Value:
  491. Ptr to next char in buffer, if successful.
  492. NULL if error writing question name.
  493. --*/
  494. {
  495. PCHAR pch;
  496. // use recursion, as default
  497. pMsg->MessageHead.RecursionDesired = TRUE;
  498. // restart write at message header
  499. pch = pMsg->MessageBody;
  500. // write question name
  501. pch = Dns_WriteDottedNameToPacket(
  502. pch,
  503. pMsg->pBufferEnd,
  504. pszName,
  505. NULL,
  506. 0,
  507. fUnicodeName );
  508. if ( !pch )
  509. {
  510. return( NULL );
  511. }
  512. // write question structure
  513. *(UNALIGNED WORD *) pch = htons( wType );
  514. pch += sizeof(WORD);
  515. *(UNALIGNED WORD *) pch = DNS_RCLASS_INTERNET;
  516. pch += sizeof(WORD);
  517. // set question RR section count
  518. pMsg->MessageHead.QuestionCount = 1;
  519. // header fields in host order
  520. pMsg->fSwapped = FALSE;
  521. // reset current ptr
  522. pMsg->pCurrent = pch;
  523. IF_DNSDBG( INIT )
  524. {
  525. DnsDbg_Message(
  526. "Packet after question write:",
  527. pMsg );
  528. }
  529. return( pch );
  530. }
  531. DNS_STATUS
  532. Dns_WriteRecordStructureToMessage(
  533. IN OUT PDNS_MSG_BUF pMsg,
  534. IN WORD wType,
  535. IN WORD wClass,
  536. IN DWORD dwTtl,
  537. IN WORD wDataLength
  538. )
  539. /*++
  540. Routine Description:
  541. No data RR cases:
  542. This includes prereqs and deletes except for specific record cases.
  543. Arguments:
  544. pch - ptr to next byte in packet buffer
  545. pchStop - end of packet buffer
  546. wClass - class
  547. wType - desired RR type
  548. dwTtl - time to live
  549. wDataLength - data length
  550. Return Value:
  551. Ptr to next postion in buffer, if successful.
  552. NULL on error.
  553. --*/
  554. {
  555. PDNS_WIRE_RECORD pdnsRR;
  556. PCHAR pchdata;
  557. IF_DNSDBG( WRITE2 )
  558. {
  559. DNS_PRINT(( "Dns_WriteRecordStructureToMessage(%p).\n", pMsg ));
  560. }
  561. //
  562. // out of space
  563. //
  564. pdnsRR = (PDNS_WIRE_RECORD) pMsg->pCurrent;
  565. pchdata = (PCHAR) pdnsRR + sizeof( DNS_WIRE_RECORD );
  566. if ( pchdata + wDataLength >= pMsg->pBufferEnd )
  567. {
  568. DNS_PRINT(( "ERROR out of space writing record to packet.\n" ));
  569. return( ERROR_MORE_DATA );
  570. }
  571. //
  572. // write RR wire structure
  573. //
  574. *(UNALIGNED WORD *) &pdnsRR->RecordType = htons( wType );
  575. *(UNALIGNED WORD *) &pdnsRR->RecordClass = htons( wClass );
  576. *(UNALIGNED DWORD *) &pdnsRR->TimeToLive = htonl( dwTtl );
  577. *(UNALIGNED WORD *) &pdnsRR->DataLength = htons( wDataLength );
  578. //
  579. // update current ptr
  580. //
  581. pMsg->pCurrent = pchdata;
  582. return( ERROR_SUCCESS );
  583. }
  584. DNS_STATUS
  585. Dns_WriteRecordStructureToPacket(
  586. IN OUT PCHAR pchBuf,
  587. IN PDNS_RECORD pRecord,
  588. IN BOOL fUpdatePacket
  589. )
  590. /*++
  591. Routine Description:
  592. Write wire record structure for given record.
  593. Arguments:
  594. pchBuf - ptr to next byte in packet buffer
  595. pRecord - record to write
  596. fUpdatePacket -- TRUE if building update message;
  597. for update the section flags in the pRecords are interpreted
  598. for update; otherwise query semantics are used
  599. Return Value:
  600. None
  601. --*/
  602. {
  603. PDNS_WIRE_RECORD pwireRecord;
  604. WORD class;
  605. DWORD ttl;
  606. IF_DNSDBG( WRITE2 )
  607. {
  608. DNS_PRINT((
  609. "Writing RR struct (%p) to packet buffer at %p.\n",
  610. pRecord,
  611. pchBuf
  612. ));
  613. DnsDbg_Record(
  614. "Record being written:",
  615. pRecord );
  616. }
  617. //
  618. // get TTL, it will be set to zero for several of the update cases
  619. //
  620. ttl = pRecord->dwTtl;
  621. //
  622. // determine class
  623. // - class variable is in net order (for perf)
  624. // - default is class IN, but may be ANY or NONE for certain updates
  625. //
  626. if ( fUpdatePacket )
  627. {
  628. switch( pRecord->Flags.S.Section )
  629. {
  630. case DNSREC_PREREQ:
  631. class = PrereqClassArray
  632. [ pRecord->wDataLength == 0 ][ pRecord->Flags.S.Delete ];
  633. ttl = 0;
  634. break;
  635. case DNSREC_UPDATE:
  636. case DNSREC_ADDITIONAL:
  637. class = UpdateClassArray
  638. [ pRecord->wDataLength == 0 ][ pRecord->Flags.S.Delete ];
  639. if ( class != DNS_RCLASS_INTERNET )
  640. {
  641. ttl = 0;
  642. }
  643. break;
  644. default:
  645. DNS_PRINT(( "ERROR: invalid RR section.\n" ));
  646. return( ERROR_INVALID_DATA );
  647. }
  648. if ( class == 0 )
  649. {
  650. DNS_PRINT(( "ERROR: no update class corresponding to RR flags.\n" ));
  651. return( ERROR_INVALID_DATA );
  652. }
  653. }
  654. else
  655. {
  656. class = DNS_RCLASS_INTERNET;
  657. }
  658. //
  659. // write RR wire structure
  660. // - zero datalength to handle no data case
  661. //
  662. pwireRecord = (PDNS_WIRE_RECORD) pchBuf;
  663. *(UNALIGNED WORD *) &pwireRecord->RecordType = htons( pRecord->wType );
  664. *(UNALIGNED WORD *) &pwireRecord->RecordClass = class;
  665. *(UNALIGNED DWORD *) &pwireRecord->TimeToLive = htonl( ttl );
  666. *(UNALIGNED WORD *) &pwireRecord->DataLength = 0;
  667. return( ERROR_SUCCESS );
  668. }
  669. PCHAR
  670. Dns_WriteRecordStructureToPacketEx(
  671. IN OUT PCHAR pchBuf,
  672. IN WORD wType,
  673. IN WORD wClass,
  674. IN DWORD dwTtl,
  675. IN WORD wDataLength
  676. )
  677. /*++
  678. Routine Description:
  679. Write wire record structure for given record.
  680. Arguments:
  681. pchBuf - ptr to next byte in packet buffer
  682. Return Value:
  683. Ptr to data section of record.
  684. --*/
  685. {
  686. PDNS_WIRE_RECORD pwireRecord;
  687. // DCR_PERF: optimize RR write to packet?
  688. pwireRecord = (PDNS_WIRE_RECORD) pchBuf;
  689. *(UNALIGNED WORD *) &pwireRecord->RecordType = htons( wType );
  690. *(UNALIGNED WORD *) &pwireRecord->RecordClass = htons( wClass );
  691. *(UNALIGNED DWORD *) &pwireRecord->TimeToLive = htonl( dwTtl );
  692. *(UNALIGNED WORD *) &pwireRecord->DataLength = htons( wDataLength );
  693. return( pchBuf + sizeof(DNS_WIRE_RECORD) );
  694. }
  695. VOID
  696. Dns_SetRecordDatalength(
  697. IN OUT PDNS_WIRE_RECORD pRecord,
  698. IN WORD wDataLength
  699. )
  700. /*++
  701. Routine Description:
  702. Reset record datalength.
  703. Arguments:
  704. pRecord - wire record to reset
  705. wDataLength - data length
  706. Return Value:
  707. Ptr to data section of record.
  708. --*/
  709. {
  710. WORD flippedWord;
  711. INLINE_WORD_FLIP( flippedWord, wDataLength );
  712. *(UNALIGNED WORD *) &pRecord->DataLength = flippedWord;
  713. }
  714. DNS_STATUS
  715. Dns_WriteOptToMessage(
  716. IN OUT PDNS_MSG_BUF pMsg,
  717. IN WORD wPayload,
  718. IN DWORD Rcode,
  719. IN BYTE Version,
  720. IN PBYTE pData,
  721. IN WORD wDataLength
  722. )
  723. /*++
  724. Routine Description:
  725. Write OPT record to message.
  726. Arguments:
  727. pMsg -- message
  728. wPayload -- max length client can receive in UDP
  729. Rcode -- RCODE, if extended some of this ends up in OPT
  730. Version -- EDNS version
  731. pData -- ptr to data buffer of OPT data
  732. wDataLength -- length of pData
  733. Return Value:
  734. ERROR_SUCCESS if successfully writen.
  735. ErrorCode on failure.
  736. --*/
  737. {
  738. DNS_STATUS status;
  739. PCHAR pstart;
  740. //
  741. // DCR: use variable OPT fields
  742. //
  743. //
  744. // save existing pCurrent
  745. // - this allows dual wire write
  746. //
  747. ASSERT( !pMsg->pPreOptEnd );
  748. pstart = pMsg->pCurrent;
  749. //
  750. // write OPT record name (root)
  751. //
  752. *pstart = 0;
  753. pMsg->pCurrent++;
  754. //
  755. // write OPT -- basic info, no options
  756. // - if OPT didn't fit, clear pPreOptEnd pointer
  757. // which serves as signal that OPT exists
  758. //
  759. status = Dns_WriteRecordStructureToMessage(
  760. pMsg,
  761. DNS_TYPE_OPT,
  762. (WORD) g_RecvBufSize, // recv buffer size (in Class)
  763. 0, // no flags\extended RCODE (in TTL)
  764. 0 // no data length
  765. );
  766. if ( status == ERROR_SUCCESS )
  767. {
  768. // increment message record count
  769. SET_TO_WRITE_ADDITIONAL_RECORDS(pMsg);
  770. CURRENT_RR_COUNT_FIELD(pMsg)++;
  771. pMsg->pPreOptEnd = pstart;
  772. }
  773. else
  774. {
  775. // on failure, reset current
  776. pMsg->pCurrent = pstart;
  777. }
  778. return( status );
  779. }
  780. DNS_STATUS
  781. Dns_WriteStandardRequestOptToMessage(
  782. IN OUT PDNS_MSG_BUF pMsg
  783. )
  784. /*++
  785. Routine Description:
  786. Write standard OPT record to message.
  787. This contains just the buffer size and version info,
  788. no error code or options.
  789. Arguments:
  790. pMsg -- message
  791. Return Value:
  792. ERROR_SUCCESS if successfully writen.
  793. ErrorCode on failure.
  794. --*/
  795. {
  796. if ( g_UseEdns == 0 )
  797. {
  798. return( ERROR_REQUEST_REFUSED );
  799. }
  800. return Dns_WriteOptToMessage(
  801. pMsg,
  802. (WORD) g_RecvBufSize,
  803. 0, // no rcode
  804. 0, // standard version
  805. NULL, // no data
  806. 0 // no data length
  807. );
  808. }
  809. DNS_STATUS
  810. Dns_AddRecordsToMessage(
  811. IN OUT PDNS_MSG_BUF pMsg,
  812. IN PDNS_RECORD pRecord,
  813. IN BOOL fUpdateMessage
  814. )
  815. /*++
  816. Routine Description:
  817. Add record or list of records to message.No data RR cases:
  818. This includes prereqs and deletes except for specific record cases.
  819. Arguments:
  820. pMsg - message buffer to write to
  821. pRecord - ptr to record (or first of list of records) to write to packet
  822. fUpdateMessage -- If TRUE, the message is going to contain an update.
  823. Therefore the section flags in the pRecord
  824. should be interpreted for update. Otherwise this is
  825. for a query message and section flags should be
  826. interpreted for query.
  827. Return Value:
  828. ERROR_SUCCESS if successful.
  829. Error code on failure.
  830. --*/
  831. {
  832. PCHAR pch = pMsg->pCurrent;
  833. PCHAR pendBuffer = pMsg->pBufferEnd;
  834. WORD currentSection = 0;
  835. WORD section;
  836. PDNS_NAME pnamePrevious = NULL;
  837. WORD compressedPreviousName;
  838. WORD offsetPreviousName;
  839. PDNS_WIRE_RECORD pwireRecord;
  840. PCHAR pchnext;
  841. WORD index;
  842. DNS_STATUS status = ERROR_SUCCESS;
  843. //
  844. // write each record in list
  845. //
  846. while ( pRecord )
  847. {
  848. //
  849. // determine section for record
  850. // - may not write to previous section
  851. section = (WORD) pRecord->Flags.S.Section;
  852. if ( section < currentSection )
  853. {
  854. DNS_PRINT((
  855. "ERROR: Attempt to write record at %p, with section %d\n"
  856. "\tless than previous section written %d.\n",
  857. pRecord,
  858. pRecord->Flags.S.Section,
  859. currentSection ));
  860. return( ERROR_INVALID_DATA );
  861. }
  862. else if ( section > currentSection )
  863. {
  864. currentSection = section;
  865. SET_CURRENT_RR_COUNT_SECTION( pMsg, section );
  866. }
  867. //
  868. // write record name
  869. // - if same as previous, write compressed name
  870. // - if first write from pRecord
  871. // - write full name
  872. // - clear reserved field for offsetting
  873. //
  874. if ( pnamePrevious && !strcmp( pnamePrevious, pRecord->pName ) )
  875. {
  876. // compression should always be BACK ptr
  877. DNS_ASSERT( offsetPreviousName < pch - (PCHAR)&pMsg->MessageHead );
  878. if ( pendBuffer <= pch + sizeof(WORD) )
  879. {
  880. return( ERROR_MORE_DATA );
  881. }
  882. if ( ! compressedPreviousName )
  883. {
  884. compressedPreviousName =
  885. htons( (WORD)(0xC000 | offsetPreviousName) );
  886. }
  887. *(UNALIGNED WORD *)pch = compressedPreviousName;
  888. pch += sizeof( WORD );
  889. }
  890. else
  891. {
  892. offsetPreviousName = (WORD)(pch - (PCHAR)&pMsg->MessageHead);
  893. compressedPreviousName = 0;
  894. pnamePrevious = pRecord->pName;
  895. pch = Dns_WriteDottedNameToPacket(
  896. pch,
  897. pendBuffer,
  898. pnamePrevious,
  899. NULL,
  900. 0,
  901. ( RECORD_CHARSET(pRecord) == DnsCharSetUnicode ) );
  902. if ( !pch )
  903. {
  904. // DCR: distinguish out of space errors from name errors during write
  905. return( DNS_ERROR_INVALID_NAME );
  906. }
  907. }
  908. //
  909. // write record structure
  910. //
  911. if ( pch + sizeof(DNS_WIRE_RECORD) >= pendBuffer )
  912. {
  913. return( ERROR_MORE_DATA );
  914. }
  915. status = Dns_WriteRecordStructureToPacket(
  916. pch,
  917. pRecord,
  918. fUpdateMessage );
  919. if ( status != ERROR_SUCCESS )
  920. {
  921. return( status );
  922. }
  923. pwireRecord = (PDNS_WIRE_RECORD) pch;
  924. pch += sizeof( DNS_WIRE_RECORD );
  925. //
  926. // record data
  927. //
  928. if ( pRecord->wDataLength )
  929. {
  930. index = INDEX_FOR_TYPE( pRecord->wType );
  931. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  932. if ( index && RRWriteTable[ index ] )
  933. {
  934. pchnext = RRWriteTable[ index ](
  935. pRecord,
  936. pch,
  937. pendBuffer
  938. );
  939. if ( ! pchnext )
  940. {
  941. status = GetLastError();
  942. DNS_PRINT((
  943. "ERROR: Record write routine failure for record type %d.\n\n"
  944. "\tstatus = %d\n",
  945. pRecord->wType,
  946. status ));
  947. return( status );
  948. }
  949. }
  950. else
  951. {
  952. // write unknown types -- as RAW data only
  953. DNS_PRINT((
  954. "WARNING: Writing unknown type %d to message\n",
  955. pRecord->wType ));
  956. if ( pendBuffer - pch <= pRecord->wDataLength )
  957. {
  958. return( ERROR_MORE_DATA );
  959. }
  960. memcpy(
  961. pch,
  962. (PCHAR) &pRecord->Data,
  963. pRecord->wDataLength );
  964. pchnext = pch + pRecord->wDataLength;
  965. }
  966. //
  967. // set packet record data length
  968. //
  969. DNS_ASSERT( (pchnext - pch) < MAXWORD );
  970. *(UNALIGNED WORD *) &pwireRecord->DataLength =
  971. htons( (WORD)(pchnext - pch) );
  972. pch = pchnext;
  973. }
  974. // increment message record count
  975. CURRENT_RR_COUNT_FIELD(pMsg)++;
  976. pRecord = pRecord->pNext;
  977. }
  978. //
  979. // resest message current ptr
  980. //
  981. pMsg->pCurrent = pch;
  982. IF_DNSDBG( INIT )
  983. {
  984. DnsDbg_Message(
  985. "Packet after adding records:",
  986. pMsg );
  987. }
  988. return( status );
  989. }
  990. PDNS_MSG_BUF
  991. Dns_BuildPacket(
  992. IN PDNS_HEADER pHeader,
  993. IN BOOL fNoHeaderCounts,
  994. IN LPSTR pszQuestionName,
  995. IN WORD wQuestionType,
  996. IN PDNS_RECORD pRecords,
  997. IN DWORD dwFlags,
  998. IN BOOL fUpdatePacket
  999. )
  1000. /*++
  1001. Routine Description:
  1002. Build packet.
  1003. Arguments:
  1004. pHeader -- DNS header to send
  1005. fNoHeaderCounts - do NOT include record counts in copying header
  1006. pszName -- DNS name to query
  1007. wType -- query type
  1008. pRecords -- address to receive ptr to record list returned from query
  1009. dwFlags -- query flags
  1010. fUpdatePacket -- If TRUE, the packet is going to contain an update.
  1011. Therefore the section flags in the pRecords
  1012. should be interpreted for update. Otherwise this is
  1013. for a query and section flags will be interpreted for
  1014. query.
  1015. Return Value:
  1016. ERROR_SUCCESS if successful.
  1017. Error code on failure.
  1018. --*/
  1019. {
  1020. PDNS_MSG_BUF pmsg;
  1021. DWORD length;
  1022. DNS_STATUS status;
  1023. DNSDBG( WRITE, (
  1024. "Dns_BuildPacket()\n"
  1025. "\tname %s\n"
  1026. "\ttype %d\n"
  1027. "\theader %p\n"
  1028. "\t - counts %d\n"
  1029. "\trecords %p\n"
  1030. "\tflags %08x\n"
  1031. "\tfUpdatePacket %d\n",
  1032. pszQuestionName,
  1033. wQuestionType,
  1034. pHeader,
  1035. fNoHeaderCounts,
  1036. pRecords,
  1037. dwFlags,
  1038. fUpdatePacket ));
  1039. //
  1040. // allocate packet
  1041. // - if just a question, standard UDP will do it
  1042. // - if contains records, then use TCP buffer
  1043. //
  1044. length = 0;
  1045. if ( pRecords )
  1046. {
  1047. length = DNS_TCP_DEFAULT_PACKET_LENGTH;
  1048. }
  1049. pmsg = Dns_AllocateMsgBuf( (WORD)length );
  1050. if ( !pmsg )
  1051. {
  1052. status = DNS_ERROR_NO_MEMORY;
  1053. goto Failed;
  1054. }
  1055. //
  1056. // write question?
  1057. //
  1058. if ( pszQuestionName )
  1059. {
  1060. if ( ! Dns_WriteQuestionToMessage(
  1061. pmsg,
  1062. pszQuestionName,
  1063. wQuestionType,
  1064. (BOOL)!!(dwFlags & DNSQUERY_UNICODE_NAME)
  1065. ) )
  1066. {
  1067. status = ERROR_INVALID_NAME;
  1068. goto Failed;
  1069. }
  1070. }
  1071. //
  1072. // build packet records
  1073. //
  1074. if ( pRecords )
  1075. {
  1076. status = Dns_AddRecordsToMessage(
  1077. pmsg,
  1078. pRecords,
  1079. fUpdatePacket );
  1080. if ( status != ERROR_SUCCESS )
  1081. {
  1082. DNS_PRINT((
  1083. "ERROR: failure writing records to message.\n" ));
  1084. goto Failed;
  1085. }
  1086. }
  1087. //
  1088. // append standard request OPT -- if possible
  1089. //
  1090. // DCR: currently no OPT for update
  1091. //
  1092. if ( !fUpdatePacket )
  1093. {
  1094. Dns_WriteStandardRequestOptToMessage( pmsg );
  1095. }
  1096. //
  1097. // overwrite header?
  1098. //
  1099. if ( pHeader )
  1100. {
  1101. length = sizeof(DNS_HEADER);
  1102. if ( fNoHeaderCounts )
  1103. {
  1104. length = sizeof(DWORD);
  1105. }
  1106. RtlCopyMemory(
  1107. & pmsg->MessageHead,
  1108. pHeader,
  1109. length );
  1110. }
  1111. // override recursion default, if desired
  1112. if ( dwFlags & DNS_QUERY_NO_RECURSION )
  1113. {
  1114. pmsg->MessageHead.RecursionDesired = FALSE;
  1115. }
  1116. // init XID if not set
  1117. if ( pmsg->MessageHead.Xid == 0 )
  1118. {
  1119. pmsg->MessageHead.Xid = gwTransactionId++;
  1120. }
  1121. return( pmsg );
  1122. Failed:
  1123. SetLastError( status );
  1124. FREE_HEAP( pmsg );
  1125. return( NULL );
  1126. }
  1127. //
  1128. // Reading from packet
  1129. //
  1130. PCHAR
  1131. _fastcall
  1132. Dns_SkipPacketName(
  1133. IN PCHAR pch,
  1134. IN PCHAR pchEnd
  1135. )
  1136. /*++
  1137. Routine Description:
  1138. Skips over name in packet
  1139. Arguments:
  1140. pch - ptr to start of name to skip
  1141. pchEnd - ptr to byte after end of packet
  1142. Return Value:
  1143. Ptr to next byte in buffer
  1144. NULL if bad name
  1145. --*/
  1146. {
  1147. register UCHAR cch;
  1148. register UCHAR cflag;
  1149. //
  1150. // Loop until end of name
  1151. //
  1152. while ( pch < pchEnd )
  1153. {
  1154. cch = *pch++;
  1155. cflag = cch & 0xC0;
  1156. //
  1157. // normal label
  1158. // - skip to next label and continue
  1159. // - stop only if at 0 (root) label
  1160. //
  1161. if ( cflag == 0 )
  1162. {
  1163. if ( cch )
  1164. {
  1165. pch += cch;
  1166. continue;
  1167. }
  1168. return( pch );
  1169. }
  1170. //
  1171. // compression
  1172. // - skip second byte in compression and return
  1173. //
  1174. else if ( cflag == 0xC0 )
  1175. {
  1176. pch++;
  1177. return( pch );
  1178. }
  1179. else
  1180. {
  1181. DNSDBG( READ, (
  1182. "ERROR: bad packet name label byte %02 at 0x%p\n",
  1183. cch,
  1184. pch - 1 ));
  1185. return( NULL );
  1186. }
  1187. }
  1188. DNSDBG( READ, (
  1189. "ERROR: packet name at %p reads past end of packet at %p\n",
  1190. pch,
  1191. pchEnd ));
  1192. return( NULL );
  1193. }
  1194. BOOL
  1195. Dns_IsSamePacketQuestion(
  1196. IN PDNS_MSG_BUF pMsg1,
  1197. IN PDNS_MSG_BUF pMsg2
  1198. )
  1199. /*++
  1200. Routine Description:
  1201. Compares questions in two messages.
  1202. Arguments:
  1203. pMsg1 -- first message
  1204. pMsg2 -- second message
  1205. Return Value:
  1206. TRUE if message questions equal.
  1207. FALSE if questions not equal.
  1208. --*/
  1209. {
  1210. PCHAR pquestion1;
  1211. PCHAR pquestion2;
  1212. PCHAR pnameEnd;
  1213. DWORD questionLength;
  1214. //
  1215. // validate and size the question fields
  1216. // - size must match
  1217. //
  1218. pquestion1 = pMsg1->MessageBody;
  1219. pnameEnd = Dns_SkipPacketName(
  1220. pquestion1,
  1221. pMsg1->pBufferEnd );
  1222. if ( !pnameEnd )
  1223. {
  1224. return FALSE;
  1225. }
  1226. questionLength = (DWORD)( pnameEnd - pquestion1 );
  1227. pquestion2 = pMsg2->MessageBody;
  1228. pnameEnd = Dns_SkipPacketName(
  1229. pquestion2,
  1230. pMsg2->pBufferEnd );
  1231. if ( !pnameEnd ||
  1232. questionLength != (DWORD)(pnameEnd - pquestion2) )
  1233. {
  1234. return FALSE;
  1235. }
  1236. //
  1237. // for speed, first do flat mem compare
  1238. // - this will hit 99% case as rarely would
  1239. // a server rewrite the question name
  1240. //
  1241. if ( RtlEqualMemory(
  1242. pquestion1,
  1243. pquestion2,
  1244. questionLength ) )
  1245. {
  1246. return TRUE;
  1247. }
  1248. //
  1249. // then do case sensitive compare
  1250. // - note, we do simple ANSI casing
  1251. // assume UTF8 extended chars MUST be downcased on the
  1252. // wire per spec
  1253. //
  1254. return !_strnicmp( pquestion1, pquestion2, questionLength );
  1255. }
  1256. PCHAR
  1257. _fastcall
  1258. Dns_SkipPacketRecord(
  1259. IN PCHAR pchRecord,
  1260. IN PCHAR pchMsgEnd
  1261. )
  1262. /*++
  1263. Routine Description:
  1264. Skips over packet RR.
  1265. This is RR structure and data, not the owner name.
  1266. Arguments:
  1267. pchRecord - ptr to start of RR structure.
  1268. pchMsgEnd - end of message
  1269. Return Value:
  1270. Ptr to next record in packet.
  1271. NULL if RR outside packet or invalid.
  1272. --*/
  1273. {
  1274. //
  1275. // skip RR struct
  1276. //
  1277. pchRecord += sizeof(DNS_WIRE_RECORD);
  1278. if ( pchRecord > pchMsgEnd )
  1279. {
  1280. return( NULL );
  1281. }
  1282. // read datalength and skip data
  1283. // datalength field is a WORD, at end of record header
  1284. pchRecord += InlineFlipUnalignedWord( pchRecord - sizeof(WORD) );
  1285. if ( pchRecord > pchMsgEnd )
  1286. {
  1287. return( NULL );
  1288. }
  1289. return( pchRecord );
  1290. }
  1291. PCHAR
  1292. Dns_SkipToRecord(
  1293. IN PDNS_HEADER pMsgHead,
  1294. IN PCHAR pMsgEnd,
  1295. IN INT iCount
  1296. )
  1297. /*++
  1298. Routine Description:
  1299. Skips over some number of DNS records.
  1300. Arguments:
  1301. pMsgHead -- ptr to begining of DNS message.
  1302. pMsgEnd -- ptr to end of DNS message
  1303. iCount -- if > 0, number of records to skip
  1304. if <= 0, number of records from end of packet
  1305. Return Value:
  1306. Ptr to next
  1307. NULL if bad packet.
  1308. --*/
  1309. {
  1310. PCHAR pch;
  1311. INT i;
  1312. WORD recordCount;
  1313. //
  1314. // determine how many records to skip
  1315. //
  1316. recordCount = pMsgHead->QuestionCount
  1317. + pMsgHead->AnswerCount
  1318. + pMsgHead->NameServerCount
  1319. + pMsgHead->AdditionalCount;
  1320. // iCount > 0 is skip count, MUST not be larger than
  1321. // actual count
  1322. if ( iCount > 0 )
  1323. {
  1324. if ( iCount > recordCount )
  1325. {
  1326. return( NULL );
  1327. }
  1328. }
  1329. // iCount <= 0 then (-iCount) is number of records
  1330. // from the last record
  1331. else
  1332. {
  1333. iCount += recordCount;
  1334. if ( iCount < 0 )
  1335. {
  1336. return( NULL );
  1337. }
  1338. }
  1339. // skip message header
  1340. pch = (PCHAR) (pMsgHead + 1);
  1341. if ( iCount == 0 )
  1342. {
  1343. return( pch );
  1344. }
  1345. //
  1346. // skip records
  1347. //
  1348. for ( i=0; i<iCount; i++ )
  1349. {
  1350. pch = Dns_SkipPacketName( pch, pMsgEnd );
  1351. if ( !pch )
  1352. {
  1353. return pch;
  1354. }
  1355. // skip question or RR
  1356. if ( i < pMsgHead->QuestionCount )
  1357. {
  1358. pch += sizeof(DNS_WIRE_QUESTION);
  1359. if ( pch > pMsgEnd )
  1360. {
  1361. return( NULL );
  1362. }
  1363. }
  1364. else
  1365. {
  1366. pch = Dns_SkipPacketRecord( pch, pMsgEnd );
  1367. if ( !pch )
  1368. {
  1369. return pch;
  1370. }
  1371. }
  1372. }
  1373. DNSDBG( READ, (
  1374. "Leaving SkipToRecord, current ptr = %p, offset = %04x\n"
  1375. "\tskipped %d records\n",
  1376. pch,
  1377. (WORD) (pch - (PCHAR)pMsgHead),
  1378. iCount ));
  1379. return( pch );
  1380. }
  1381. PCHAR
  1382. _fastcall
  1383. Dns_ReadPacketName(
  1384. IN OUT PCHAR pchNameBuffer,
  1385. OUT PWORD pwNameLength,
  1386. IN OUT PWORD pwNameOffset, OPTIONAL
  1387. OUT PBOOL pfNameSameAsPrevious, OPTIONAL
  1388. IN PCHAR pchName,
  1389. IN PCHAR pchStart,
  1390. IN PCHAR pchEnd
  1391. )
  1392. /*++
  1393. Routine Description:
  1394. Reads packet name and converts it to DNS-dotted format.
  1395. Arguments:
  1396. pchNameBuffer - buffer to write name into; contains previous name, if any
  1397. pwNameLength - length of name written to buffer; does not include
  1398. terminating NULL
  1399. pwNameOffset - addr with previous names offset (zero if no previous name);
  1400. on return, contains this name's offset
  1401. OPTIONAL but must exist if fNameSameAsPrevious exists
  1402. pfNameSameAsPrevious - addr of flag set if this name is same as previous;
  1403. OPTIONAL but must exist if pwNameOffset exists
  1404. pchName - ptr to name in packet
  1405. pchStart - start of DNS message
  1406. pchEnd - ptr to byte after end of DNS message
  1407. Return Value:
  1408. Ptr to next byte in packet.
  1409. NULL on error.
  1410. --*/
  1411. {
  1412. register PUCHAR pch = pchName;
  1413. register UCHAR cch;
  1414. register UCHAR cflag;
  1415. PCHAR pchdotted;
  1416. PCHAR pbufferEnd;
  1417. PCHAR pchreturn = NULL;
  1418. DNS_ASSERT( pch > pchStart && pchEnd > pchStart );
  1419. DNS_ASSERT( (pwNameOffset && pfNameSameAsPrevious) ||
  1420. (!pwNameOffset && !pfNameSameAsPrevious) );
  1421. //
  1422. // read through labels and/or compression until reach end of name
  1423. //
  1424. pbufferEnd = pchNameBuffer + DNS_MAX_NAME_LENGTH;
  1425. pchdotted = pchNameBuffer;
  1426. while ( pch < pchEnd )
  1427. {
  1428. cch = *pch++;
  1429. //
  1430. // at root label
  1431. // - if root name, write single '.'
  1432. // - otherwise strip trailing dot from last label
  1433. // - save length written
  1434. // - NULL teminate name
  1435. // - set same as previous FALSE
  1436. // - save packet offset to this name
  1437. // - return next byte in buffer
  1438. //
  1439. if ( cch == 0 )
  1440. {
  1441. if ( pchdotted == pchNameBuffer )
  1442. {
  1443. *pchdotted++ = '.';
  1444. }
  1445. else
  1446. {
  1447. pchdotted--;
  1448. }
  1449. *pwNameLength = (WORD)(pchdotted - pchNameBuffer);
  1450. *pchdotted = 0;
  1451. if ( pwNameOffset )
  1452. {
  1453. *pfNameSameAsPrevious = FALSE;
  1454. *pwNameOffset = (WORD)(pchName - pchStart);
  1455. }
  1456. return( pchreturn ? pchreturn : pch );
  1457. }
  1458. cflag = cch & 0xC0;
  1459. //
  1460. // regular label
  1461. // - copy label to buffer
  1462. // - jump to next label
  1463. if ( cflag == 0 )
  1464. {
  1465. PCHAR pchnext = pch + cch;
  1466. if ( pchnext >= pchEnd )
  1467. {
  1468. DNS_PRINT((
  1469. "ERROR: Packet name at %p extends past end of buffer\n",
  1470. pchName ));
  1471. goto Failed;
  1472. }
  1473. if ( pchdotted + cch + 1 >= pbufferEnd )
  1474. {
  1475. DNS_PRINT((
  1476. "ERROR: Packet name at %p exceeds max length\n",
  1477. pchName ));
  1478. goto Failed;
  1479. }
  1480. memcpy(
  1481. pchdotted,
  1482. pch,
  1483. cch );
  1484. pchdotted += cch;
  1485. *pchdotted++ = '.';
  1486. pch = pchnext;
  1487. continue;
  1488. }
  1489. //
  1490. // offset
  1491. // - get offset
  1492. // - if offset at start of name compare to previous name offset
  1493. // - otherwise follow offset to build new name
  1494. //
  1495. if ( cflag == 0xC0 )
  1496. {
  1497. WORD offset;
  1498. PCHAR pchoffset;
  1499. offset = (cch ^ 0xC0);
  1500. offset <<= 8;
  1501. offset |= *pch;
  1502. pchoffset = --pch;
  1503. //
  1504. // first offset
  1505. // - save return pointer
  1506. //
  1507. // if name is entirely offset
  1508. // - same as previous offset -- done
  1509. // - if not still save this offset rather than offset
  1510. // to name itself (first answer is usually just offset
  1511. // to question, subsequent answer RRs continue to reference
  1512. // question offset, not first answer)
  1513. //
  1514. if ( !pchreturn )
  1515. {
  1516. DNS_ASSERT( pch >= pchName );
  1517. pchreturn = pch+2;
  1518. if ( pchoffset == pchName && pwNameOffset )
  1519. {
  1520. if ( *pwNameOffset == offset )
  1521. {
  1522. *pfNameSameAsPrevious = TRUE;
  1523. return( pchreturn );
  1524. }
  1525. else
  1526. {
  1527. // save offset that comprises name
  1528. // then kill out copy of return ptr so don't
  1529. // return offset to pchName when finish copy
  1530. *pwNameOffset = offset;
  1531. *pfNameSameAsPrevious = FALSE;
  1532. pwNameOffset = NULL;
  1533. }
  1534. }
  1535. }
  1536. //
  1537. // make jump to new bytes and continue
  1538. // - verify offset is BEFORE current name
  1539. // and BEFORE current ptr
  1540. pch = pchStart + offset;
  1541. if ( pch >= pchName || pch >= pchoffset )
  1542. {
  1543. DNS_PRINT((
  1544. "ERROR: Bogus name offset %d, encountered at %p\n"
  1545. "\tto location %p past current position or original name.\n",
  1546. offset,
  1547. pchoffset,
  1548. pch ));
  1549. goto Failed;
  1550. }
  1551. continue;
  1552. }
  1553. // any other label byte is bogus
  1554. else
  1555. {
  1556. DNS_PRINT((
  1557. "ERROR: bogus name label byte %02x at %p\n",
  1558. cch,
  1559. pch - 1 ));
  1560. goto Failed;
  1561. }
  1562. }
  1563. DNS_PRINT((
  1564. "ERROR: packet name at %p reads to ptr %p past end of packet at %p\n",
  1565. pchName,
  1566. pch,
  1567. pchEnd ));
  1568. //
  1569. // failed
  1570. // - return NULL
  1571. // - set OUT params, keeps prefix happy on higher level calls
  1572. //
  1573. Failed:
  1574. *pwNameLength = 0;
  1575. if ( pwNameOffset )
  1576. {
  1577. *pwNameOffset = 0;
  1578. }
  1579. if ( pfNameSameAsPrevious )
  1580. {
  1581. *pfNameSameAsPrevious = FALSE;
  1582. }
  1583. return ( NULL );
  1584. }
  1585. PCHAR
  1586. _fastcall
  1587. Dns_ReadPacketNameAllocate(
  1588. IN OUT PCHAR * ppchName,
  1589. OUT PWORD pwNameLength,
  1590. IN OUT PWORD pwPrevNameOffset, OPTIONAL
  1591. OUT PBOOL pfNameSameAsPrevious, OPTIONAL
  1592. IN PCHAR pchPacketName,
  1593. IN PCHAR pchStart,
  1594. IN PCHAR pchEnd
  1595. )
  1596. /*++
  1597. Routine Description:
  1598. Reads packet name and creates (allocates) a copy in DNS-dotted format.
  1599. Arguments:
  1600. ppchName - addr to recv resulting name ptr
  1601. pwNameLength - length of name written to buffer
  1602. pwNameOffset - addr with previous names offset (zero if no previous name);
  1603. on return, contains this name's offset
  1604. OPTIONAL but must exist if fNameSameAsPrevious exists
  1605. fNameSameAsPrevious - addr of flag set if this name is same as previous;
  1606. OPTIONAL but must exist if pwNameOffset exists
  1607. pchPacketName - pch to name in packet
  1608. pchStart - start of DNS message
  1609. pchEnd - ptr to byte after end of DNS message
  1610. Return Value:
  1611. Ptr to next byte in packet.
  1612. NULL on error.
  1613. --*/
  1614. {
  1615. PCHAR pch;
  1616. PCHAR pallocName;
  1617. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1618. WORD nameLength = DNS_MAX_NAME_BUFFER_LENGTH;
  1619. //
  1620. // read packet name into buffer
  1621. //
  1622. pch = Dns_ReadPacketName(
  1623. nameBuffer,
  1624. & nameLength,
  1625. pwPrevNameOffset,
  1626. pfNameSameAsPrevious,
  1627. pchPacketName,
  1628. pchStart,
  1629. pchEnd );
  1630. if ( !pch )
  1631. {
  1632. return( pch );
  1633. }
  1634. //
  1635. // allocate buffer for packet name
  1636. // - nameLength does not include terminating NULL
  1637. //
  1638. nameLength++;
  1639. pallocName = (PCHAR) ALLOCATE_HEAP( nameLength );
  1640. if ( !pallocName )
  1641. {
  1642. return( NULL );
  1643. }
  1644. RtlCopyMemory(
  1645. pallocName,
  1646. nameBuffer,
  1647. nameLength );
  1648. *ppchName = pallocName;
  1649. *pwNameLength = --nameLength;
  1650. DNSDBG( READ, (
  1651. "Allocated copy of packet name %s length %d\n",
  1652. pallocName,
  1653. nameLength ));
  1654. return( pch );
  1655. }
  1656. DNS_STATUS
  1657. Dns_ExtractRecordsFromMessage(
  1658. IN PDNS_MSG_BUF pMsg,
  1659. IN BOOL fUnicode,
  1660. OUT PDNS_RECORD * ppRecord
  1661. )
  1662. /*++
  1663. Routine Description:
  1664. Extract records from packet.
  1665. Arguments:
  1666. pMsg - message buffer to write to
  1667. fUnicode - flag indicating strings in record should be unicode
  1668. Return Value:
  1669. Ptr to parsed record list if any.
  1670. NULL if no record list or error.
  1671. --*/
  1672. {
  1673. PDNS_MESSAGE_BUFFER pDnsBuffer = (PDNS_MESSAGE_BUFFER) &pMsg->MessageHead;
  1674. return Dns_ExtractRecordsFromBuffer(
  1675. pDnsBuffer,
  1676. pMsg->MessageLength,
  1677. fUnicode,
  1678. ppRecord );
  1679. }
  1680. DNS_STATUS
  1681. Dns_ParseMessage(
  1682. OUT PDNS_PARSED_MESSAGE pParse,
  1683. IN PDNS_MESSAGE_BUFFER pDnsBuffer,
  1684. IN WORD wMessageLength,
  1685. IN DWORD Flags,
  1686. IN DNS_CHARSET OutCharSet
  1687. )
  1688. /*++
  1689. Routine Description:
  1690. Parse DNS message.
  1691. Arguments:
  1692. pParse - ptr to blob to receive parsed message
  1693. pDnsBuffer - message buffer to read from
  1694. wMessageLength -- message length
  1695. Flags - parsing options
  1696. OutCharSet - DNS character set; only UTF8 and unicode supported
  1697. Return Value:
  1698. RCODE error status on successful parse (including NO_ERROR)
  1699. DNS_INFO_NO_RECORDS -- on auth-empty response
  1700. // referral
  1701. DNS_ERROR_BAD_PACKET -- on bad packet
  1702. Note: even on failure caller must free data
  1703. --*/
  1704. {
  1705. register PCHAR pch;
  1706. PDNS_HEADER pwireMsg = (PDNS_HEADER) pDnsBuffer;
  1707. PCHAR pchpacketEnd;
  1708. DNS_PARSED_RR parsedRR;
  1709. LPSTR pnameOwner;
  1710. LPSTR pnameNew = NULL;
  1711. PWORD pCurrentCountField = NULL;
  1712. WORD countRR;
  1713. WORD countSection;
  1714. WORD typePrevious = 0;
  1715. WORD nameOffset = 0;
  1716. WORD nameLength;
  1717. WORD type;
  1718. WORD index;
  1719. BYTE section;
  1720. BOOL fnameSameAsPrevious;
  1721. PDNS_RECORD pnewRR;
  1722. DNS_RRSET rrset;
  1723. DNS_RRSET rrsetAlias;
  1724. DNS_RRSET rrsetSig;
  1725. DNS_STATUS status;
  1726. DNS_STATUS rcodeStatus;
  1727. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1728. DNS_RECORD recordTemp;
  1729. DNSDBG( READ, (
  1730. "Dns_ParseMessage( %p, len=%d )\n",
  1731. pDnsBuffer,
  1732. wMessageLength
  1733. ));
  1734. //
  1735. // clear parsing blob
  1736. //
  1737. RtlZeroMemory(
  1738. pParse,
  1739. sizeof(DNS_PARSED_MESSAGE) );
  1740. //
  1741. // only UTF8 or unicode is supported directly
  1742. //
  1743. if ( OutCharSet != DnsCharSetUnicode &&
  1744. OutCharSet != DnsCharSetUtf8 )
  1745. {
  1746. ASSERT( FALSE );
  1747. return( ERROR_INVALID_PARAMETER );
  1748. }
  1749. //
  1750. // error code
  1751. // - map RCODE to DNS error
  1752. // - if other than NAME_ERROR, don't bother parsing
  1753. //
  1754. // DCR: option to parse with other errors
  1755. //
  1756. rcodeStatus = pwireMsg->ResponseCode;
  1757. if ( rcodeStatus != 0 )
  1758. {
  1759. rcodeStatus = Dns_MapRcodeToStatus( pwireMsg->ResponseCode );
  1760. if ( rcodeStatus != DNS_ERROR_RCODE_NAME_ERROR &&
  1761. !(Flags & DNS_PARSE_FLAG_RCODE_ALL) )
  1762. {
  1763. DNSDBG( READ, (
  1764. "No records extracted from response\n"
  1765. "\tresponse code = %d\n",
  1766. pwireMsg->ResponseCode ));
  1767. return( rcodeStatus );
  1768. }
  1769. }
  1770. //
  1771. // clear record holders
  1772. // - do now so safe in bad packet cleanup
  1773. //
  1774. DNS_RRSET_INIT( rrset );
  1775. DNS_RRSET_INIT( rrsetAlias );
  1776. DNS_RRSET_INIT( rrsetSig );
  1777. //
  1778. // copy message header
  1779. //
  1780. RtlCopyMemory(
  1781. & pParse->Header,
  1782. pwireMsg,
  1783. sizeof(DNS_HEADER) );
  1784. //
  1785. // read RRs in list of records
  1786. //
  1787. // loop through all resource records
  1788. // - skip question
  1789. // - build DNS_RECORD structure for other records
  1790. //
  1791. pchpacketEnd = (PCHAR)pwireMsg + wMessageLength;
  1792. pch = pDnsBuffer->MessageBody;
  1793. section = DNSREC_QUESTION;
  1794. pCurrentCountField = &pwireMsg->QuestionCount;
  1795. countSection = pwireMsg->QuestionCount;
  1796. countRR = 0;
  1797. while( 1 )
  1798. {
  1799. //
  1800. // changing sections
  1801. // save section number and RR count for current section
  1802. // note need immediate loop back to handle empty section
  1803. //
  1804. countRR++;
  1805. if ( countRR > countSection )
  1806. {
  1807. if ( section == DNSREC_QUESTION )
  1808. {
  1809. // no-op
  1810. }
  1811. else if ( section == DNSREC_ANSWER )
  1812. {
  1813. pParse->pAnswerRecords = rrset.pFirstRR;
  1814. }
  1815. else if ( section == DNSREC_AUTHORITY )
  1816. {
  1817. pParse->pAuthorityRecords = rrset.pFirstRR;
  1818. }
  1819. else if ( section == DNSREC_ADDITIONAL )
  1820. {
  1821. pParse->pAdditionalRecords = rrset.pFirstRR;
  1822. break;
  1823. }
  1824. section++;
  1825. pCurrentCountField++;
  1826. countSection = *(pCurrentCountField);
  1827. countRR = 0;
  1828. typePrevious = 0; // force new RR set
  1829. DNS_RRSET_INIT( rrset );
  1830. continue;
  1831. }
  1832. // validity check next RR
  1833. if ( pch >= pchpacketEnd )
  1834. {
  1835. DNS_PRINT((
  1836. "ERROR: reading bad packet %p.\n"
  1837. "\tat end of packet length with more records to process\n"
  1838. "\tpacket length = %ld\n"
  1839. "\tcurrent offset = %ld\n",
  1840. wMessageLength,
  1841. pch - (PCHAR)pwireMsg
  1842. ));
  1843. goto PacketError;
  1844. }
  1845. //
  1846. // read name, determining if same as previous name
  1847. //
  1848. IF_DNSDBG( READ2 )
  1849. {
  1850. DnsDbg_Lock();
  1851. DNS_PRINT((
  1852. "Reading record at offset %x\n",
  1853. (WORD)(pch - (PCHAR)pwireMsg) ));
  1854. DnsDbg_PacketName(
  1855. "Record name ",
  1856. pch,
  1857. pwireMsg,
  1858. pchpacketEnd,
  1859. "\n" );
  1860. DnsDbg_Unlock();
  1861. }
  1862. pch = Dns_ReadPacketName(
  1863. nameBuffer,
  1864. & nameLength,
  1865. & nameOffset,
  1866. & fnameSameAsPrevious,
  1867. pch,
  1868. (PCHAR) pwireMsg,
  1869. pchpacketEnd );
  1870. if ( ! pch )
  1871. {
  1872. DNS_PRINT(( "ERROR: bad packet name.\n" ));
  1873. goto PacketError;
  1874. }
  1875. IF_DNSDBG( READ2 )
  1876. {
  1877. DNS_PRINT((
  1878. "Owner name of record %s\n"
  1879. "\tlength = %d\n"
  1880. "\toffset = %d\n"
  1881. "\tfSameAsPrevious = %d\n",
  1882. nameBuffer,
  1883. nameLength,
  1884. nameOffset,
  1885. fnameSameAsPrevious ));
  1886. }
  1887. //
  1888. // question
  1889. //
  1890. if ( section == DNSREC_QUESTION )
  1891. {
  1892. PSTR pnameQuestion = NULL;
  1893. if ( !(Flags & DNS_PARSE_FLAG_NO_QUESTION) )
  1894. {
  1895. pnameQuestion = Dns_NameCopyAllocate(
  1896. nameBuffer,
  1897. (UCHAR) nameLength,
  1898. DnsCharSetUtf8, // UTF8 in
  1899. OutCharSet
  1900. );
  1901. }
  1902. pParse->pQuestionName = (LPTSTR) pnameQuestion;
  1903. if ( pch + sizeof(DNS_WIRE_QUESTION) > pchpacketEnd )
  1904. {
  1905. DNS_PRINT(( "ERROR: question exceeds packet length.\n" ));
  1906. goto PacketError;
  1907. }
  1908. pParse->QuestionType = InlineFlipUnalignedWord( pch );
  1909. pch += sizeof(WORD);
  1910. pParse->QuestionClass = InlineFlipUnalignedWord( pch );
  1911. pch += sizeof(WORD);
  1912. if ( Flags & DNS_PARSE_FLAG_ONLY_QUESTION )
  1913. {
  1914. break;
  1915. }
  1916. continue;
  1917. }
  1918. //
  1919. // extract RR info, type, datalength
  1920. // - verify RR within message
  1921. //
  1922. pch = Dns_ReadRecordStructureFromPacket(
  1923. pch,
  1924. pchpacketEnd,
  1925. & parsedRR );
  1926. if ( !pch )
  1927. {
  1928. DNS_PRINT(( "ERROR: bad RR struct out of packet.\n" ));
  1929. goto PacketError;
  1930. }
  1931. type = parsedRR.Type;
  1932. //
  1933. // type change -- then have new RR set
  1934. // - setup for new name
  1935. // - check and see if first non-alias answer
  1936. //
  1937. if ( type != typePrevious )
  1938. {
  1939. fnameSameAsPrevious = FALSE;
  1940. typePrevious = type;
  1941. }
  1942. //
  1943. // screen out OPT
  1944. //
  1945. // DCR: make screening configurable for API
  1946. //
  1947. if ( type == DNS_TYPE_OPT )
  1948. {
  1949. continue;
  1950. }
  1951. //
  1952. // screen out SIGs -- if not desired
  1953. //
  1954. #if 0
  1955. if ( type == DNS_TYPE_SIG &&
  1956. flag & NOSIG )
  1957. {
  1958. continue;
  1959. }
  1960. #endif
  1961. //
  1962. // make copy of new name
  1963. //
  1964. // DCR_FIX0: name same as previous
  1965. // flag indicates only that name not compressed to previous
  1966. // name (or previous compression)
  1967. // actually need abolute ingnore case compare
  1968. // with last records name to be sure that name not previous
  1969. //
  1970. if ( !fnameSameAsPrevious )
  1971. {
  1972. pnameNew = Dns_NameCopyAllocate(
  1973. nameBuffer,
  1974. (UCHAR) nameLength,
  1975. DnsCharSetUtf8, // UTF8 string in
  1976. OutCharSet
  1977. );
  1978. if ( !pnameNew )
  1979. {
  1980. status = DNS_ERROR_NO_MEMORY;
  1981. goto Failed;
  1982. }
  1983. pnameOwner = pnameNew;
  1984. DNSDBG( OFF, (
  1985. "Copy of owner name of record being read from packet %s\n",
  1986. nameBuffer ));
  1987. }
  1988. DNS_ASSERT( pnameOwner );
  1989. DNS_ASSERT( pnameNew || fnameSameAsPrevious );
  1990. //
  1991. // TSIG record requires owner name for versioning
  1992. //
  1993. recordTemp.pName = pnameOwner;
  1994. //
  1995. // read RR data for type
  1996. //
  1997. index = INDEX_FOR_TYPE( type );
  1998. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  1999. if ( !index || !RRReadTable[ index ] )
  2000. {
  2001. // unknown types -- index to NULL type to use
  2002. // FlatRecordRead()
  2003. DNS_PRINT((
  2004. "WARNING: Reading unknown record type %d from message\n",
  2005. parsedRR.Type ));
  2006. index = DNS_TYPE_NULL;
  2007. }
  2008. pnewRR = RRReadTable[ index ](
  2009. &recordTemp,
  2010. OutCharSet,
  2011. (PCHAR) pDnsBuffer,
  2012. parsedRR.pchData,
  2013. pch // end of record data
  2014. );
  2015. if ( ! pnewRR )
  2016. {
  2017. status = GetLastError();
  2018. ASSERT( status != ERROR_SUCCESS );
  2019. DNS_PRINT((
  2020. "ERROR: RRReadTable routine failure for type %d.\n"
  2021. "\tstatus = %d\n"
  2022. "\tpchdata = %p\n"
  2023. "\tpchend = %p\n",
  2024. parsedRR.Type,
  2025. status,
  2026. parsedRR.pchData,
  2027. pch ));
  2028. if ( status == ERROR_SUCCESS )
  2029. {
  2030. status = DNS_ERROR_NO_MEMORY;
  2031. }
  2032. goto Failed;
  2033. }
  2034. //
  2035. // write record info
  2036. // - first RR in set gets new name allocation
  2037. // and is responsible for cleanup
  2038. // - no data cleanup necessary as all data is
  2039. // contained in the RR allocation
  2040. //
  2041. pnewRR->pName = pnameOwner;
  2042. pnewRR->wType = type;
  2043. pnewRR->dwTtl = parsedRR.Ttl;
  2044. pnewRR->Flags.S.Section = section;
  2045. pnewRR->Flags.S.CharSet = OutCharSet;
  2046. FLAG_FreeOwner( pnewRR ) = !fnameSameAsPrevious;
  2047. FLAG_FreeData( pnewRR ) = FALSE;
  2048. //
  2049. // add RR to list
  2050. //
  2051. if ( type == DNS_TYPE_SIG )
  2052. {
  2053. DNS_RRSET_ADD( rrsetSig, pnewRR );
  2054. }
  2055. else if ( type == DNS_TYPE_CNAME &&
  2056. pParse->QuestionType != DNS_TYPE_ALL &&
  2057. pParse->QuestionType != DNS_TYPE_CNAME &&
  2058. section == DNSREC_ANSWER )
  2059. {
  2060. DNS_RRSET_ADD( rrsetAlias, pnewRR );
  2061. }
  2062. else
  2063. {
  2064. DNS_RRSET_ADD( rrset, pnewRR );
  2065. }
  2066. // clear new ptr, as name now part of record
  2067. // this is strictly used to determine when pnameOwner
  2068. // must be cleaned up on failure
  2069. pnameNew = NULL;
  2070. } // end loop through packet's records
  2071. //
  2072. // set response info
  2073. //
  2074. // DCR: if don't want single SIG, easy to break out by section
  2075. //
  2076. pParse->pAliasRecords = rrsetAlias.pFirstRR;
  2077. pParse->pSigRecords = rrsetSig.pFirstRR;
  2078. //
  2079. // break out various query NO_ERROR responses
  2080. // - empty response
  2081. // - referral
  2082. // - garbage
  2083. //
  2084. if ( pwireMsg->AnswerCount == 0 &&
  2085. rcodeStatus == 0 &&
  2086. pwireMsg->Opcode == DNS_OPCODE_QUERY &&
  2087. pwireMsg->IsResponse )
  2088. {
  2089. PDNS_RECORD prrAuth = pParse->pAuthorityRecords;
  2090. if ( (prrAuth && prrAuth->wType == DNS_TYPE_SOA) ||
  2091. (!prrAuth && pwireMsg->Authoritative) )
  2092. {
  2093. rcodeStatus = DNS_INFO_NO_RECORDS;
  2094. DNSDBG( READ, ( "Empty-auth response at %p.\n", pwireMsg ));
  2095. }
  2096. else if ( prrAuth &&
  2097. prrAuth->wType == DNS_TYPE_NS &&
  2098. !pwireMsg->Authoritative &&
  2099. (!pwireMsg->RecursionAvailable || !pwireMsg->RecursionDesired) )
  2100. {
  2101. rcodeStatus = DNS_ERROR_REFERRAL_RESPONSE;
  2102. DNSDBG( READ, ( "Referral response at %p.\n", pwireMsg ));
  2103. }
  2104. else
  2105. {
  2106. rcodeStatus = DNS_ERROR_BAD_PACKET;
  2107. DNSDBG( ANY, ( "Bogus NO_ERROR response at %p.\n", pwireMsg ));
  2108. DNS_ASSERT( FALSE );
  2109. }
  2110. }
  2111. // verify never turn RCODE result into SUCCESS
  2112. ASSERT( pwireMsg->ResponseCode == 0 || rcodeStatus != ERROR_SUCCESS );
  2113. ASSERT( pnameNew == NULL );
  2114. pParse->Status = rcodeStatus;
  2115. IF_DNSDBG( RECV )
  2116. {
  2117. DnsDbg_ParsedMessage(
  2118. "Parsed message:\n",
  2119. pParse );
  2120. }
  2121. return( rcodeStatus );
  2122. PacketError:
  2123. DNS_PRINT(( "ERROR: bad packet in buffer.\n" ));
  2124. status = DNS_ERROR_BAD_PACKET;
  2125. Failed:
  2126. FREE_HEAP( pnameNew );
  2127. Dns_RecordListFree( rrset.pFirstRR );
  2128. Dns_RecordListFree( rrsetAlias.pFirstRR );
  2129. Dns_RecordListFree( rrsetSig.pFirstRR );
  2130. pParse->Status = status;
  2131. return( status );
  2132. }
  2133. VOID
  2134. Dns_FreeParsedMessageFields(
  2135. IN OUT PDNS_PARSED_MESSAGE pParse
  2136. )
  2137. /*++
  2138. Routine Description:
  2139. Free a parsed DNS message struct.
  2140. Arguments:
  2141. pParse - ptr to blob to receive parsed message
  2142. Return Value:
  2143. None
  2144. --*/
  2145. {
  2146. DNSDBG( TRACE, (
  2147. "Dns_FreeParsedMessageFields( %p )\n",
  2148. pParse ));
  2149. // question name
  2150. FREE_HEAP( pParse->pQuestionName );
  2151. // records
  2152. Dns_RecordListFree( pParse->pAliasRecords );
  2153. Dns_RecordListFree( pParse->pAnswerRecords );
  2154. Dns_RecordListFree( pParse->pAdditionalRecords );
  2155. Dns_RecordListFree( pParse->pAuthorityRecords );
  2156. Dns_RecordListFree( pParse->pSigRecords );
  2157. // clear to avoid confusion or double free
  2158. RtlZeroMemory(
  2159. pParse,
  2160. sizeof(DNS_PARSED_MESSAGE) );
  2161. }
  2162. DNS_STATUS
  2163. Dns_ExtractRecordsFromBuffer(
  2164. IN PDNS_MESSAGE_BUFFER pDnsBuffer,
  2165. IN WORD wMessageLength,
  2166. IN BOOL fUnicode,
  2167. OUT PDNS_RECORD * ppRecord
  2168. )
  2169. /*++
  2170. Routine Description:
  2171. Extract records from packet buffer.
  2172. Arguments:
  2173. pDnsBuffer - message buffer to read from
  2174. fUnicode - flag indicating strings in record should be unicode
  2175. Return Value:
  2176. Ptr to parsed record list if any.
  2177. NULL if no record list or error.
  2178. --*/
  2179. {
  2180. PDNS_RECORD prr;
  2181. DNS_STATUS status;
  2182. DNS_PARSED_MESSAGE parseBlob;
  2183. DNSDBG( READ, (
  2184. "Dns_ExtractRecordsFromBuffer( %p, len=%d )\n",
  2185. pDnsBuffer,
  2186. wMessageLength
  2187. ));
  2188. //
  2189. // call real parsing function
  2190. //
  2191. status = Dns_ParseMessage(
  2192. & parseBlob,
  2193. pDnsBuffer,
  2194. wMessageLength,
  2195. DNS_PARSE_FLAG_NO_QUESTION,
  2196. fUnicode
  2197. ? DnsCharSetUnicode
  2198. : DnsCharSetUtf8
  2199. );
  2200. //
  2201. // concatentate into one blob
  2202. // - work backwards so only touch each record once
  2203. //
  2204. prr = Dns_RecordListAppend(
  2205. parseBlob.pAuthorityRecords,
  2206. parseBlob.pAdditionalRecords
  2207. );
  2208. prr = Dns_RecordListAppend(
  2209. parseBlob.pAnswerRecords,
  2210. prr
  2211. );
  2212. prr = Dns_RecordListAppend(
  2213. parseBlob.pAliasRecords,
  2214. prr
  2215. );
  2216. *ppRecord = prr;
  2217. IF_DNSDBG( RECV )
  2218. {
  2219. DnsDbg_RecordSet(
  2220. "Extracted records:\n",
  2221. prr );
  2222. }
  2223. return( status );
  2224. }
  2225. #if 0
  2226. DNS_STATUS
  2227. Dns_ExtractRecordsFromBuffer(
  2228. IN PDNS_MESSAGE_BUFFER pDnsBuffer,
  2229. IN WORD wMessageLength,
  2230. IN BOOL fUnicode,
  2231. OUT PDNS_RECORD * ppRecord
  2232. )
  2233. /*++
  2234. Routine Description:
  2235. Extract records from packet buffer.
  2236. Arguments:
  2237. pDnsBuffer - message buffer to read from
  2238. fUnicode - flag indicating strings in record should be unicode
  2239. Return Value:
  2240. Ptr to parsed record list if any.
  2241. NULL if no record list or error.
  2242. --*/
  2243. {
  2244. register PCHAR pch;
  2245. PDNS_HEADER pwireMsg = (PDNS_HEADER) pDnsBuffer;
  2246. PCHAR pchpacketEnd;
  2247. DNS_PARSED_RR parsedRR;
  2248. LPSTR pnameOwner;
  2249. LPSTR pnameNew = NULL;
  2250. DNS_CHARSET outCharSet;
  2251. WORD countRR;
  2252. WORD countSection;
  2253. WORD typePrevious = 0;
  2254. WORD nameOffset = 0;
  2255. WORD nameLength;
  2256. WORD index;
  2257. BYTE section;
  2258. BOOL fnameSameAsPrevious;
  2259. PDNS_RECORD pnewRR;
  2260. DNS_RRSET rrset;
  2261. DNS_STATUS status;
  2262. DNS_STATUS rcodeStatus;
  2263. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  2264. DNS_RECORD recordTemp;
  2265. PWORD pCurrentCountField = NULL;
  2266. DNSDBG( READ, (
  2267. "Dns_ExtractRecordsFromBuffer( %p, len=%d )\n",
  2268. pDnsBuffer,
  2269. wMessageLength
  2270. ));
  2271. //
  2272. // error code
  2273. // - map RCODE to DNS error
  2274. // - if other than NAME_ERROR, don't bother parsing
  2275. //
  2276. rcodeStatus = pwireMsg->ResponseCode;
  2277. if ( rcodeStatus != 0 )
  2278. {
  2279. rcodeStatus = Dns_MapRcodeToStatus( pwireMsg->ResponseCode );
  2280. if ( rcodeStatus != DNS_ERROR_RCODE_NAME_ERROR )
  2281. {
  2282. DNSDBG( READ, (
  2283. "No records extracted from response\n"
  2284. "\tresponse code = %d\n",
  2285. pwireMsg->ResponseCode ));
  2286. return( rcodeStatus );
  2287. }
  2288. }
  2289. DNS_RRSET_INIT( rrset );
  2290. //
  2291. // detemine char set
  2292. //
  2293. if ( fUnicode )
  2294. {
  2295. outCharSet = DnsCharSetUnicode;
  2296. }
  2297. else
  2298. {
  2299. outCharSet = DnsCharSetUtf8;
  2300. }
  2301. //
  2302. // read RRs in list of records
  2303. //
  2304. // loop through all resource records
  2305. // - skip question
  2306. // - build DNS_RECORD structure for other records
  2307. //
  2308. pchpacketEnd = (PCHAR)pwireMsg + wMessageLength;
  2309. pch = pDnsBuffer->MessageBody;
  2310. section = DNSREC_QUESTION;
  2311. pCurrentCountField = &pwireMsg->QuestionCount;
  2312. countSection = pwireMsg->QuestionCount;
  2313. countRR = 0;
  2314. while( 1 )
  2315. {
  2316. //
  2317. // changing sections
  2318. // save section number and RR count for current section
  2319. // note need immediate loop back to handle empty section
  2320. //
  2321. countRR++;
  2322. if ( countRR > countSection )
  2323. {
  2324. if ( section == DNSREC_ADDITIONAL )
  2325. {
  2326. break;
  2327. }
  2328. section++;
  2329. pCurrentCountField++;
  2330. countSection = *(pCurrentCountField);
  2331. countRR = 0;
  2332. continue;
  2333. }
  2334. // validity check next RR
  2335. if ( pch >= pchpacketEnd )
  2336. {
  2337. DNS_PRINT((
  2338. "ERROR: reading bad packet %p.\n"
  2339. "\tat end of packet length with more records to process\n"
  2340. "\tpacket length = %ld\n"
  2341. "\tcurrent offset = %ld\n",
  2342. pDnsBuffer,
  2343. wMessageLength,
  2344. pch - (PCHAR)pwireMsg
  2345. ));
  2346. goto PacketError;
  2347. }
  2348. //
  2349. // skip question
  2350. //
  2351. if ( section == DNSREC_QUESTION )
  2352. {
  2353. pch = Dns_SkipPacketName(
  2354. pch,
  2355. pchpacketEnd );
  2356. if ( !pch )
  2357. {
  2358. DNS_PRINT(( "ERROR: bad question name.\n" ));
  2359. goto PacketError;
  2360. }
  2361. pch += sizeof(DNS_WIRE_QUESTION);
  2362. if ( pch > pchpacketEnd )
  2363. {
  2364. DNS_PRINT(( "ERROR: question exceeds packet length.\n" ));
  2365. goto PacketError;
  2366. }
  2367. continue;
  2368. }
  2369. //
  2370. // read name, determining if same as previous name
  2371. //
  2372. IF_DNSDBG( READ2 )
  2373. {
  2374. DnsDbg_Lock();
  2375. DNS_PRINT((
  2376. "Reading record at offset %x\n",
  2377. (WORD)(pch - (PCHAR)pwireMsg) ));
  2378. DnsDbg_PacketName(
  2379. "Record name ",
  2380. pch,
  2381. pwireMsg,
  2382. pchpacketEnd,
  2383. "\n" );
  2384. DnsDbg_Unlock();
  2385. }
  2386. pch = Dns_ReadPacketName(
  2387. nameBuffer,
  2388. & nameLength,
  2389. & nameOffset,
  2390. & fnameSameAsPrevious,
  2391. pch,
  2392. (PCHAR) pwireMsg,
  2393. pchpacketEnd );
  2394. if ( ! pch )
  2395. {
  2396. DNS_PRINT(( "ERROR: bad packet name.\n" ));
  2397. goto PacketError;
  2398. }
  2399. IF_DNSDBG( READ2 )
  2400. {
  2401. DNS_PRINT((
  2402. "Owner name of record %s\n"
  2403. "\tlength = %d\n"
  2404. "\toffset = %d\n"
  2405. "\tfSameAsPrevious = %d\n",
  2406. nameBuffer,
  2407. nameLength,
  2408. nameOffset,
  2409. fnameSameAsPrevious ));
  2410. }
  2411. //
  2412. // extract RR info, type, datalength
  2413. // - verify RR within message
  2414. //
  2415. pch = Dns_ReadRecordStructureFromPacket(
  2416. pch,
  2417. pchpacketEnd,
  2418. & parsedRR );
  2419. if ( !pch )
  2420. {
  2421. DNS_PRINT(( "ERROR: bad RR struct out of packet.\n" ));
  2422. goto PacketError;
  2423. }
  2424. //
  2425. // on type change, always have new RR set
  2426. // - setup for new name
  2427. //
  2428. if ( parsedRR.Type != typePrevious )
  2429. {
  2430. fnameSameAsPrevious = FALSE;
  2431. typePrevious = parsedRR.Type;
  2432. }
  2433. //
  2434. // screen out OPT
  2435. //
  2436. // DCR: make screening configurable for API
  2437. //
  2438. if ( parsedRR.Type == DNS_TYPE_OPT )
  2439. {
  2440. continue;
  2441. }
  2442. //
  2443. // make copy of new name
  2444. //
  2445. // DCR_FIX0: name same as previous
  2446. // flag indicates only that name not compressed to previous
  2447. // name (or previous compression)
  2448. // actually need abolute ingnore case compare
  2449. // with last records name to be sure that name not previous
  2450. //
  2451. if ( !fnameSameAsPrevious )
  2452. {
  2453. pnameNew = Dns_NameCopyAllocate(
  2454. nameBuffer,
  2455. (UCHAR) nameLength,
  2456. DnsCharSetUtf8, // UTF8 string in
  2457. outCharSet
  2458. );
  2459. pnameOwner = pnameNew;
  2460. DNSDBG( READ2, (
  2461. "Copy of owner name of record being read from packet %s\n",
  2462. pnameOwner ));
  2463. }
  2464. DNS_ASSERT( pnameOwner );
  2465. DNS_ASSERT( pnameNew || fnameSameAsPrevious );
  2466. //
  2467. // TSIG record requires owner name for versioning
  2468. //
  2469. recordTemp.pName = pnameOwner;
  2470. //
  2471. // read RR data for type
  2472. //
  2473. index = INDEX_FOR_TYPE( parsedRR.Type );
  2474. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  2475. if ( !index || !RRReadTable[ index ] )
  2476. {
  2477. // unknown types -- index to NULL type to use
  2478. // FlatRecordRead()
  2479. DNS_PRINT((
  2480. "WARNING: Reading unknown record type %d from message\n",
  2481. parsedRR.Type ));
  2482. index = DNS_TYPE_NULL;
  2483. }
  2484. pnewRR = RRReadTable[ index ](
  2485. &recordTemp,
  2486. outCharSet,
  2487. (PCHAR) pDnsBuffer,
  2488. parsedRR.pchData,
  2489. pch // end of record data
  2490. );
  2491. if ( ! pnewRR )
  2492. {
  2493. status = GetLastError();
  2494. ASSERT( status != ERROR_SUCCESS );
  2495. DNS_PRINT((
  2496. "ERROR: RRReadTable routine failure for type %d.\n"
  2497. "\tstatus = %d\n"
  2498. "\tpchdata = %p\n"
  2499. "\tpchend = %p\n",
  2500. parsedRR.Type,
  2501. status,
  2502. parsedRR.pchData,
  2503. pch ));
  2504. if ( status == ERROR_SUCCESS )
  2505. {
  2506. status = DNS_ERROR_NO_MEMORY;
  2507. }
  2508. goto Failed;
  2509. }
  2510. //
  2511. // write record info
  2512. // - first RR in set gets new name allocation
  2513. // and is responsible for cleanup
  2514. // - no data cleanup necessary as all data is
  2515. // contained in the RR allocation
  2516. //
  2517. pnewRR->pName = pnameOwner;
  2518. pnewRR->wType = parsedRR.Type;
  2519. pnewRR->dwTtl = parsedRR.Ttl;
  2520. pnewRR->Flags.S.Section = section;
  2521. pnewRR->Flags.S.CharSet = outCharSet;
  2522. FLAG_FreeOwner( pnewRR ) = !fnameSameAsPrevious;
  2523. FLAG_FreeData( pnewRR ) = FALSE;
  2524. // add RR to list
  2525. DNS_RRSET_ADD( rrset, pnewRR );
  2526. // clear new ptr, as name now part of record
  2527. // this is strictly used to determine when pnameOwner
  2528. // must be cleaned up on failure
  2529. pnameNew = NULL;
  2530. } // end loop through packet's records
  2531. //
  2532. // return parsed record list
  2533. // - return DNS error for RCODE
  2534. // - set special return code to differentiate empty response
  2535. //
  2536. // DCR: should have special REFERRAL response
  2537. // - could overload NOTAUTH rcode
  2538. // DCR: should have special EMPTY_AUTH response
  2539. // - could have empty-auth overload NXRRSET
  2540. //
  2541. // DCR: best check on distinguishing EMPTY_AUTH from REFERRAL
  2542. //
  2543. if ( pwireMsg->AnswerCount == 0 && rcodeStatus == 0 )
  2544. {
  2545. if ( !rrset.pFirstRR || rrset.pFirstRR->wType == DNS_TYPE_SOA )
  2546. {
  2547. rcodeStatus = DNS_INFO_NO_RECORDS;
  2548. DNSDBG( READ, ( "Empty-auth response at %p.\n", pwireMsg ));
  2549. }
  2550. #if 0
  2551. else if ( rrset.pFirstRR->wType == DNS_TYPE_NS &&
  2552. !pwireMsg->Authoritative )
  2553. {
  2554. rcodeStatus = DNS_INFO_REFERRAL;
  2555. DNSDBG( READ, ( "Referral response at %p.\n", pwireMsg ));
  2556. }
  2557. else
  2558. {
  2559. rcodeStatus = DNS_ERROR_BAD_PACKET;
  2560. DNSDBG( READ, ( "Bogus NO_ERROR response at %p.\n", pwireMsg ));
  2561. }
  2562. #endif
  2563. }
  2564. // verify never turn RCODE result into SUCCESS
  2565. ASSERT( pwireMsg->ResponseCode == 0 || rcodeStatus != ERROR_SUCCESS );
  2566. ASSERT( pnameNew == NULL );
  2567. *ppRecord = rrset.pFirstRR;
  2568. IF_DNSDBG( RECV )
  2569. {
  2570. DnsDbg_RecordSet(
  2571. "Extracted records:\n",
  2572. *ppRecord );
  2573. }
  2574. return( rcodeStatus );
  2575. PacketError:
  2576. DNS_PRINT(( "ERROR: bad packet in buffer.\n" ));
  2577. status = DNS_ERROR_BAD_PACKET;
  2578. Failed:
  2579. FREE_HEAP( pnameNew );
  2580. Dns_RecordListFree( rrset.pFirstRR );
  2581. return( status );
  2582. }
  2583. #endif
  2584. VOID
  2585. Dns_NormalizeAllRecordTtls(
  2586. IN OUT PDNS_RECORD pRecord
  2587. )
  2588. /*++
  2589. Routine Description:
  2590. Finds the lowest TTL value in RR set and the sets all
  2591. records to that value.
  2592. Arguments:
  2593. pRecord - record set to normalize ttl values of.
  2594. Return Value:
  2595. None
  2596. --*/
  2597. {
  2598. PDNS_RECORD pTemp = pRecord;
  2599. DWORD dwTtl;
  2600. WORD wType;
  2601. //
  2602. // Get the Ttl of the first record (if there is one)
  2603. //
  2604. if ( pTemp )
  2605. {
  2606. dwTtl = pTemp->dwTtl;
  2607. wType = pTemp->wType;
  2608. pTemp = pTemp->pNext;
  2609. }
  2610. //
  2611. // Walk any remaining records looking for an even lower ttl value
  2612. //
  2613. while ( pTemp &&
  2614. pTemp->wType == wType &&
  2615. pTemp->Flags.S.Section == DNSREC_ANSWER )
  2616. {
  2617. if ( pTemp->dwTtl < dwTtl )
  2618. {
  2619. dwTtl = pTemp->dwTtl;
  2620. }
  2621. pTemp = pTemp->pNext;
  2622. }
  2623. //
  2624. // Set all records to this lowest ttl value
  2625. //
  2626. pTemp = pRecord;
  2627. while ( pTemp &&
  2628. pTemp->wType == wType &&
  2629. pTemp->Flags.S.Section == DNSREC_ANSWER )
  2630. {
  2631. pTemp->dwTtl = dwTtl;
  2632. pTemp = pTemp->pNext;
  2633. }
  2634. }
  2635. PCHAR
  2636. Dns_ReadRecordStructureFromPacket(
  2637. IN PCHAR pchPacket,
  2638. IN PCHAR pchMsgEnd,
  2639. IN OUT PDNS_PARSED_RR pParsedRR
  2640. )
  2641. /*++
  2642. Routine Description:
  2643. Read record structure from packet.
  2644. Arguments:
  2645. pchPacket - ptr to record structure in packet
  2646. pchMsgEnd - end of message
  2647. pParsedRR - ptr to struct to receive parsed RR
  2648. Return Value:
  2649. Ptr to next record in packet -- based on datalength.
  2650. Null on error.
  2651. --*/
  2652. {
  2653. PCHAR pch = pchPacket;
  2654. DNSDBG( READ2, (
  2655. "Dns_ReadRecordStructureFromPacket(%p).\n",
  2656. pch ));
  2657. //
  2658. // verify record structure within packet
  2659. //
  2660. if ( pch + sizeof(DNS_WIRE_RECORD) > pchMsgEnd )
  2661. {
  2662. DNS_PRINT((
  2663. "ERROR: record structure at %p is not within packet!.\n",
  2664. pchPacket ));
  2665. return( 0 );
  2666. }
  2667. //
  2668. // flip fields and write to aligned struct
  2669. //
  2670. pParsedRR->pchRR = pch;
  2671. pParsedRR->Type = InlineFlipUnalignedWord( pch );
  2672. pch += sizeof(WORD);
  2673. pParsedRR->Class = InlineFlipUnalignedWord( pch );
  2674. pch += sizeof(WORD);
  2675. pParsedRR->Ttl = InlineFlipUnalignedDword( pch );
  2676. pch += sizeof(DWORD);
  2677. pParsedRR->DataLength = InlineFlipUnalignedWord( pch );
  2678. pch += sizeof(WORD);
  2679. pParsedRR->pchData = pch;
  2680. //
  2681. // verify datalength does not extend beyond packet end
  2682. //
  2683. pch += pParsedRR->DataLength;
  2684. pParsedRR->pchNextRR = pch;
  2685. if ( pch > pchMsgEnd )
  2686. {
  2687. DNS_PRINT((
  2688. "ERROR: record data at %p (length %d) is not within packet!.\n",
  2689. pch - pParsedRR->DataLength,
  2690. pParsedRR->DataLength ));
  2691. return( 0 );
  2692. }
  2693. //
  2694. // return ptr to next record in packet
  2695. //
  2696. return( pch );
  2697. }
  2698. PCHAR
  2699. Dns_ParsePacketRecord(
  2700. IN PCHAR pchPacket,
  2701. IN PCHAR pchMsgEnd,
  2702. IN OUT PDNS_PARSED_RR pParsedRR
  2703. )
  2704. /*++
  2705. Routine Description:
  2706. Read record from packet.
  2707. Arguments:
  2708. pchPacket - ptr to record structure in packet
  2709. pchMsgEnd - end of message
  2710. pParsedRR - ptr to struct to receive parsed RR
  2711. Return Value:
  2712. Ptr to next record in packet -- based on datalength.
  2713. Null on error.
  2714. --*/
  2715. {
  2716. PCHAR pch;
  2717. DNSDBG( READ2, (
  2718. "Dns_ParsePacketRecord().\n"
  2719. "\tpRecordStart = %p\n"
  2720. "\tpMsgEnd = %p\n",
  2721. pchPacket,
  2722. pchMsgEnd ));
  2723. //
  2724. // save and skip name
  2725. //
  2726. pch = Dns_SkipPacketName(
  2727. pchPacket,
  2728. pchMsgEnd );
  2729. if ( !pch )
  2730. {
  2731. return( pch );
  2732. }
  2733. pParsedRR->pchName = pchPacket;
  2734. //
  2735. // parse record structure
  2736. //
  2737. pch = Dns_ReadRecordStructureFromPacket(
  2738. pch,
  2739. pchMsgEnd,
  2740. pParsedRR );
  2741. return( pch );
  2742. }
  2743. //
  2744. // Random packet utilities
  2745. //
  2746. WORD
  2747. Dns_GetRandomXid(
  2748. IN PVOID pSeed
  2749. )
  2750. /*++
  2751. Routine Description:
  2752. Generate "random" XID.
  2753. Arguments:
  2754. pSeed -- seed ptr; from stack or heap, provides differentiation beyond time
  2755. Return Value:
  2756. XID generated
  2757. --*/
  2758. {
  2759. SYSTEMTIME systemTime;
  2760. //
  2761. // may have multiple sessions to different processes\threads
  2762. //
  2763. // use system time hashed in with seed pointer
  2764. // ptr is first pushed down to count 64-bit boundaries, so lack of
  2765. // randomness in last 6bits is not preserved
  2766. //
  2767. GetSystemTime( &systemTime );
  2768. // hash millisecs with arbitrary stack location after knocking off any
  2769. // 64-bit boundary constraints
  2770. return (WORD)( systemTime.wMilliseconds * (PtrToUlong(pSeed) >> 6) );
  2771. }
  2772. //
  2773. // End packet.c
  2774. //