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.

2551 lines
56 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. packet.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  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. // Class values for UPDATE packets
  21. // (Key concept here -- designed by committee)
  22. //
  23. // These arrays are indexed by
  24. // !wDataLength -- row
  25. // Delete flag -- column
  26. //
  27. WORD PrereqClassArray[2][2] =
  28. {
  29. DNS_RCLASS_INTERNET, // data != 0, no delete
  30. 0, // data != 0, delete => ERROR
  31. DNS_RCLASS_ANY, // no data, no delete
  32. DNS_RCLASS_NONE, // no data, delete
  33. };
  34. WORD UpdateClassArray[2][2] =
  35. {
  36. DNS_RCLASS_INTERNET, // data != 0, no delete
  37. DNS_RCLASS_NONE, // data != 0, delete
  38. 0, // no data, no delete => ERROR
  39. DNS_RCLASS_ANY, // no data, delete
  40. };
  41. PDNS_MSG_BUF
  42. Dns_AllocateMsgBuf(
  43. IN WORD wBufferLength OPTIONAL
  44. )
  45. /*++
  46. Routine Description:
  47. Allocate message buffer.
  48. Arguments:
  49. wBufferLength - optional length of message buffer; default is MAX
  50. UDP size
  51. Return Value:
  52. Ptr to message buffer.
  53. NULL on error.
  54. --*/
  55. {
  56. PDNS_MSG_BUF pmsg;
  57. BOOL ftcp = FALSE;
  58. //
  59. // default allocation to UDP max buffer length
  60. //
  61. if ( wBufferLength <= DNS_MAX_UDP_PACKET_BUFFER_LENGTH )
  62. {
  63. wBufferLength = DNS_MAX_UDP_PACKET_BUFFER_LENGTH;
  64. }
  65. else
  66. {
  67. ftcp = TRUE;
  68. }
  69. pmsg = ALLOCATE_HEAP( SIZEOF_MSG_BUF_OVERHEAD + wBufferLength );
  70. if ( pmsg == NULL )
  71. {
  72. return( NULL );
  73. }
  74. pmsg->BufferLength = wBufferLength;
  75. //
  76. // limit UDP sends to classical RFC UDP limit (512 bytes)
  77. // write routines use pBufferEnd to determine writeability
  78. //
  79. if ( !ftcp )
  80. {
  81. wBufferLength = DNS_RFC_MAX_UDP_PACKET_LENGTH;
  82. }
  83. pmsg->pBufferEnd = (PCHAR)&pmsg->MessageHead + wBufferLength;
  84. pmsg->fTcp = (BOOLEAN)ftcp;
  85. Dns_InitializeMsgBuf( pmsg );
  86. return( pmsg );
  87. }
  88. VOID
  89. Dns_InitializeMsgBuf(
  90. IN OUT PDNS_MSG_BUF pMsg
  91. )
  92. /*++
  93. Routine Description:
  94. Allocate message buffer.
  95. Arguments:
  96. wBufferLength - optional length of message buffer; default is MAX
  97. UDP size
  98. Return Value:
  99. Ptr to message buffer.
  100. NULL on error.
  101. --*/
  102. {
  103. // setup addressing info
  104. pMsg->Socket = 0;
  105. pMsg->RemoteAddressLength = sizeof(SOCKADDR_IN);
  106. // set for packet reception
  107. if ( pMsg->fTcp )
  108. {
  109. SET_MESSAGE_FOR_TCP_RECV( pMsg );
  110. }
  111. else
  112. {
  113. SET_MESSAGE_FOR_UDP_RECV( pMsg );
  114. }
  115. // clear header
  116. RtlZeroMemory(
  117. &pMsg->MessageHead,
  118. sizeof( DNS_HEADER ) );
  119. // set for rewriting
  120. pMsg->pCurrent = pMsg->MessageBody;
  121. }
  122. //
  123. // Writing to packet
  124. //
  125. PCHAR
  126. _fastcall
  127. Dns_WriteDottedNameToPacket(
  128. IN OUT PCHAR pch,
  129. IN PCHAR pchStop,
  130. IN LPSTR pszName,
  131. IN LPSTR pszDomain, OPTIONAL
  132. IN WORD wDomainOffset, OPTIONAL
  133. IN BOOL fUnicodeName
  134. )
  135. /*++
  136. Routine Description:
  137. Write lookup name to packet.
  138. Arguments:
  139. pch -- ptr to current position in packet buffer
  140. pchStop -- end of packet buffer
  141. pszName - dotted FQDN to write
  142. pszDomain - domain name already in packet (OPTIONAL); note this is
  143. a fragment of the SAME STRING as pszName; i.e. ptr compare
  144. NOT strcmp is done to determine if at domain name
  145. wDomainOffset - offset in packet of domain name; MUST include this
  146. if pszDomain is given
  147. fUnicodeName -- pszName is unicode string
  148. TRUE -- name is unicode
  149. FALSE -- name is UTF8
  150. Return Value:
  151. Ptr to next position in packet buffer.
  152. NULL on error.
  153. --*/
  154. {
  155. CHAR ch;
  156. PCHAR pchlabelStart;
  157. UCHAR countLabel = 0;
  158. ULONG countName = 0;
  159. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  160. WCHAR nameWideBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  161. // protect message buffer overrun
  162. if ( pch >= pchStop )
  163. {
  164. return( NULL );
  165. }
  166. // allow root to be indicated by NULL
  167. if ( !pszName )
  168. {
  169. *pch++ = 0;
  170. return( pch );
  171. }
  172. //
  173. // protect stack buffers from bogus names
  174. //
  175. if ( fUnicodeName )
  176. {
  177. countName = wcslen( (LPWSTR) pszName );
  178. }
  179. else
  180. {
  181. countName = strlen( pszName );
  182. }
  183. if ( countName >= DNS_MAX_NAME_BUFFER_LENGTH )
  184. {
  185. return NULL;
  186. }
  187. countName = 0;
  188. //
  189. // UTF8 name with extended chars?
  190. // - then must go up to unicode for downcasing
  191. //
  192. if ( !fUnicodeName )
  193. {
  194. if ( !Dns_IsStringAscii( pszName ) )
  195. {
  196. DWORD bufLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  197. if ( ! Dns_NameCopy(
  198. (PCHAR) nameWideBuffer,
  199. & bufLength,
  200. pszName,
  201. 0, // length unknown
  202. DnsCharSetUtf8,
  203. DnsCharSetUnicode
  204. ) )
  205. {
  206. return( NULL );
  207. }
  208. pszName = (PCHAR) nameWideBuffer;
  209. fUnicodeName = TRUE;
  210. }
  211. }
  212. //
  213. // convert unicode name to UTF8
  214. // - if extended chars, then downcase before hit the wire
  215. //
  216. if ( fUnicodeName )
  217. {
  218. if ( !Dns_IsWideStringAscii( (PWSTR)pszName ) )
  219. {
  220. Dns_DownCaseBuffW(
  221. (PWCHAR) pszName,
  222. 0 );
  223. }
  224. if ( ! Dns_NameCopy(
  225. nameBuffer,
  226. NULL, // assume adequate length
  227. pszName,
  228. 0, // length unknown
  229. DnsCharSetUnicode,
  230. DnsCharSetUtf8
  231. ) )
  232. {
  233. return( NULL );
  234. }
  235. pszName = nameBuffer;
  236. }
  237. //
  238. // special case "." root name
  239. // - allows us to fail all other zero length labels cleanly
  240. //
  241. if ( *pszName == '.' )
  242. {
  243. if ( *(pszName+1) != 0 )
  244. {
  245. return( NULL );
  246. }
  247. *pch++ = 0;
  248. return( pch );
  249. }
  250. //
  251. // write lookup name
  252. // - leave
  253. pchlabelStart = pch++;
  254. while( ch = *pszName++ )
  255. {
  256. // out of space
  257. if ( pch >= pchStop )
  258. {
  259. return( NULL );
  260. }
  261. // not at end of label -- just copy character
  262. if ( ch != '.' )
  263. {
  264. *pch++ = ch;
  265. countLabel++;
  266. countName++;
  267. continue;
  268. }
  269. //
  270. // at end of label
  271. // - write label count
  272. // - reset counter
  273. // - if reached domain name, write compression and quit
  274. //
  275. if ( countLabel > DNS_MAX_LABEL_LENGTH ||
  276. countLabel == 0 ||
  277. countName > DNS_MAX_NAME_LENGTH )
  278. {
  279. return( NULL );
  280. }
  281. *pchlabelStart = countLabel;
  282. countLabel = 0;
  283. countName++;
  284. pchlabelStart = pch++;
  285. if ( pszName == pszDomain )
  286. {
  287. if ( ++pch >= pchStop )
  288. {
  289. return( NULL );
  290. }
  291. *(UNALIGNED WORD *)pchlabelStart =
  292. htons( (WORD)(wDomainOffset | (WORD)0xC000) );
  293. return( pch );
  294. }
  295. }
  296. if ( countLabel > DNS_MAX_LABEL_LENGTH ||
  297. countName > DNS_MAX_NAME_LENGTH )
  298. {
  299. return( NULL );
  300. }
  301. //
  302. // NULL terminate
  303. // - if no terminating ".", do end of label processing also
  304. // - return ptr to char after terminating NULL
  305. if ( countLabel )
  306. {
  307. *pchlabelStart = countLabel;
  308. *pch++ = 0;
  309. }
  310. else
  311. {
  312. DNS_ASSERT( pch == pchlabelStart + 1 );
  313. *pchlabelStart = 0;
  314. }
  315. return( pch );
  316. }
  317. PCHAR
  318. _fastcall
  319. Dns_WriteStringToPacket(
  320. IN OUT PCHAR pch,
  321. IN PCHAR pchStop,
  322. IN LPSTR pszString,
  323. IN BOOL fUnicodeString
  324. )
  325. /*++
  326. Routine Description:
  327. Write string to packet.
  328. Arguments:
  329. pch -- ptr to current position in packet buffer
  330. pchStop -- end of packet buffer
  331. pszString - string to write
  332. fUnicodeString -- pszString is unicode string
  333. Return Value:
  334. Ptr to next position in packet buffer.
  335. NULL on error.
  336. --*/
  337. {
  338. DWORD length;
  339. //
  340. // handle NULL string
  341. //
  342. if ( !pszString )
  343. {
  344. if ( pch >= pchStop )
  345. {
  346. return( NULL );
  347. }
  348. *pch++ = 0;
  349. return( pch );
  350. }
  351. //
  352. // get string length
  353. // - get required buf length, then whack whack off space
  354. // for terminating NULL
  355. // - zero error case, becomes very large number and is
  356. // caught by length>MAXCHAR test
  357. //
  358. length = Dns_GetBufferLengthForStringCopy(
  359. pszString,
  360. 0,
  361. fUnicodeString ? DnsCharSetUnicode : DnsCharSetUtf8,
  362. DnsCharSetUtf8 );
  363. length--;
  364. //
  365. // set length byte
  366. //
  367. if ( length > MAXUCHAR )
  368. {
  369. SetLastError( ERROR_INVALID_DATA );
  370. return( NULL );
  371. }
  372. *pch++ = (UCHAR) length;
  373. if ( pch + length > pchStop )
  374. {
  375. SetLastError( ERROR_MORE_DATA );
  376. return( NULL );
  377. }
  378. //
  379. // copy string
  380. //
  381. // note unicode conversion will write NULL terminator, so
  382. // test again for space in packet
  383. //
  384. if ( fUnicodeString )
  385. {
  386. if ( pch + length + 1 > pchStop )
  387. {
  388. SetLastError( ERROR_MORE_DATA );
  389. return( NULL );
  390. }
  391. Dns_StringCopy(
  392. pch,
  393. NULL,
  394. pszString,
  395. length,
  396. DnsCharSetUnicode,
  397. DnsCharSetUtf8 );
  398. }
  399. else
  400. {
  401. memcpy(
  402. pch,
  403. pszString,
  404. length );
  405. }
  406. return( pch+length );
  407. }
  408. PCHAR
  409. Dns_WriteQuestionToMessage(
  410. IN OUT PDNS_MSG_BUF pMsg,
  411. IN PDNS_NAME pszName,
  412. IN WORD wType,
  413. IN BOOL fUnicodeName
  414. )
  415. /*++
  416. Routine Description:
  417. Write question to packet.
  418. Note: Routine DOES NOT clear message info or message header.
  419. This is optimized for use immediately following Dns_CreateMessage().
  420. Arguments:
  421. pMsg - message info
  422. pszName - DNS name of question
  423. wType - question type
  424. fUnicodeName - name is in unicode
  425. Return Value:
  426. Ptr to next char in buffer, if successful.
  427. NULL if error writing question name.
  428. --*/
  429. {
  430. PCHAR pch;
  431. // use recursion, as default
  432. pMsg->MessageHead.RecursionDesired = TRUE;
  433. // restart write at message header
  434. pch = pMsg->MessageBody;
  435. // write question name
  436. pch = Dns_WriteDottedNameToPacket(
  437. pch,
  438. pMsg->pBufferEnd,
  439. pszName,
  440. NULL,
  441. 0,
  442. fUnicodeName );
  443. if ( !pch )
  444. {
  445. return( NULL );
  446. }
  447. // write question structure
  448. *(UNALIGNED WORD *) pch = htons( wType );
  449. pch += sizeof(WORD);
  450. *(UNALIGNED WORD *) pch = DNS_RCLASS_INTERNET;
  451. pch += sizeof(WORD);
  452. // set question RR section count
  453. pMsg->MessageHead.QuestionCount = 1;
  454. // header fields in host order
  455. pMsg->fSwapped = FALSE;
  456. // reset current ptr
  457. pMsg->pCurrent = pch;
  458. IF_DNSDBG( INIT )
  459. {
  460. DnsDbg_Message(
  461. "Packet after question write:",
  462. pMsg );
  463. }
  464. return( pch );
  465. }
  466. DNS_STATUS
  467. Dns_WriteRecordStructureToMessage(
  468. IN OUT PDNS_MSG_BUF pMsg,
  469. IN WORD wType,
  470. IN WORD wClass,
  471. IN DWORD dwTtl,
  472. IN WORD wDataLength
  473. )
  474. /*++
  475. Routine Description:
  476. No data RR cases:
  477. This includes prereqs and deletes except for specific record cases.
  478. Arguments:
  479. pch - ptr to next byte in packet buffer
  480. pchStop - end of packet buffer
  481. wClass - class
  482. wType - desired RR type
  483. dwTtl - time to live
  484. wDataLength - data length
  485. Return Value:
  486. Ptr to next postion in buffer, if successful.
  487. NULL on error.
  488. --*/
  489. {
  490. PDNS_WIRE_RECORD pdnsRR;
  491. PCHAR pchdata;
  492. IF_DNSDBG( WRITE2 )
  493. {
  494. DNS_PRINT(( "Dns_WriteRecordStructureToMessage(%p).\n", pMsg ));
  495. }
  496. //
  497. // out of space
  498. //
  499. pdnsRR = (PDNS_WIRE_RECORD) pMsg->pCurrent;
  500. pchdata = (PCHAR) pdnsRR + sizeof( DNS_WIRE_RECORD );
  501. if ( pchdata + wDataLength >= pMsg->pBufferEnd )
  502. {
  503. DNS_PRINT(( "ERROR out of space writing record to packet.\n" ));
  504. return( ERROR_MORE_DATA );
  505. }
  506. //
  507. // write RR wire structure
  508. //
  509. *(UNALIGNED WORD *) &pdnsRR->RecordType = htons( wType );
  510. *(UNALIGNED WORD *) &pdnsRR->RecordClass = htons( wClass );
  511. *(UNALIGNED DWORD *) &pdnsRR->TimeToLive = htonl( dwTtl );
  512. *(UNALIGNED WORD *) &pdnsRR->DataLength = htons( wDataLength );
  513. //
  514. // update current ptr
  515. //
  516. pMsg->pCurrent = pchdata;
  517. return( ERROR_SUCCESS );
  518. }
  519. #if 0
  520. DNS_STATUS
  521. Dns_WriteOptToMessage(
  522. IN OUT PDNS_MSG_BUF pMsg,
  523. IN WORD wPayload
  524. IN DWORD Rcode,
  525. IN BYTE Version,
  526. IN PBYTE pData,
  527. IN WORD wDataLength
  528. )
  529. /*++
  530. Routine Description:
  531. Write OPT record to message.
  532. Arguments:
  533. pMsg -- message
  534. wPayload -- max length client can receive in UDP
  535. Rcode -- RCODE, if extended some of this ends up in OPT
  536. Version -- EDNS version
  537. pData -- ptr to data buffer of OPT data
  538. wDataLength -- length of pData
  539. Return Value:
  540. ERROR_SUCCESS if successfully writen.
  541. ErrorCode on failure.
  542. --*/
  543. {
  544. }
  545. #endif
  546. DNS_STATUS
  547. Dns_WriteRecordStructureToPacket(
  548. IN OUT PCHAR pchBuf,
  549. IN PDNS_RECORD pRecord,
  550. IN BOOL fUpdatePacket
  551. )
  552. /*++
  553. Routine Description:
  554. Write wire record structure for given record.
  555. Arguments:
  556. pchBuf - ptr to next byte in packet buffer
  557. pRecord - record to write
  558. fUpdatePacket -- If TRUE, the packet is going to contain an update.
  559. Therefore the section flags in the pRecords
  560. should be interpreted for update. Otherwise this is
  561. for a query and section flags will be interpreted for
  562. query.
  563. Return Value:
  564. None
  565. --*/
  566. {
  567. PDNS_WIRE_RECORD pwireRecord;
  568. WORD class;
  569. DWORD ttl;
  570. IF_DNSDBG( WRITE2 )
  571. {
  572. DNS_PRINT((
  573. "Writing RR struct (%p) to packet buffer at %p.\n",
  574. pRecord,
  575. pchBuf
  576. ));
  577. DnsDbg_Record(
  578. "Record being written:",
  579. pRecord );
  580. }
  581. //
  582. // get TTL, it will be set to zero for several of the update cases
  583. //
  584. ttl = pRecord->dwTtl;
  585. //
  586. // determine class
  587. // - class variable is in net order (for perf)
  588. // - default is class IN, but may be ANY or NONE for certain updates
  589. //
  590. if ( fUpdatePacket )
  591. {
  592. switch( pRecord->Flags.S.Section )
  593. {
  594. case DNSREC_PREREQ:
  595. class = PrereqClassArray
  596. [ pRecord->wDataLength == 0 ][ pRecord->Flags.S.Delete ];
  597. ttl = 0;
  598. break;
  599. case DNSREC_UPDATE:
  600. case DNSREC_ADDITIONAL:
  601. class = UpdateClassArray
  602. [ pRecord->wDataLength == 0 ][ pRecord->Flags.S.Delete ];
  603. if ( class != DNS_RCLASS_INTERNET )
  604. {
  605. ttl = 0;
  606. }
  607. break;
  608. default:
  609. DNS_PRINT(( "ERROR: invalid RR section.\n" ));
  610. return( ERROR_INVALID_DATA );
  611. }
  612. if ( class == 0 )
  613. {
  614. DNS_PRINT(( "ERROR: no update class corresponding to RR flags.\n" ));
  615. return( ERROR_INVALID_DATA );
  616. }
  617. }
  618. else
  619. {
  620. class = DNS_RCLASS_INTERNET;
  621. }
  622. //
  623. // write RR wire structure
  624. // - zero datalength to handle no data case
  625. //
  626. pwireRecord = (PDNS_WIRE_RECORD) pchBuf;
  627. *(UNALIGNED WORD *) &pwireRecord->RecordType = htons( pRecord->wType );
  628. *(UNALIGNED WORD *) &pwireRecord->RecordClass = class;
  629. *(UNALIGNED DWORD *) &pwireRecord->TimeToLive = htonl( ttl );
  630. *(UNALIGNED WORD *) &pwireRecord->DataLength = 0;
  631. return( ERROR_SUCCESS );
  632. }
  633. PCHAR
  634. Dns_WriteRecordStructureToPacketEx(
  635. IN OUT PCHAR pchBuf,
  636. IN WORD wType,
  637. IN WORD wClass,
  638. IN DWORD dwTtl,
  639. IN WORD wDataLength
  640. )
  641. /*++
  642. Routine Description:
  643. Write wire record structure for given record.
  644. Arguments:
  645. pchBuf - ptr to next byte in packet buffer
  646. Return Value:
  647. Ptr to data section of record.
  648. --*/
  649. {
  650. PDNS_WIRE_RECORD pwireRecord;
  651. // DCR_PERF: optimize RR write to packet?
  652. pwireRecord = (PDNS_WIRE_RECORD) pchBuf;
  653. *(UNALIGNED WORD *) &pwireRecord->RecordType = htons( wType );
  654. *(UNALIGNED WORD *) &pwireRecord->RecordClass = htons( wClass );
  655. *(UNALIGNED DWORD *) &pwireRecord->TimeToLive = htonl( dwTtl );
  656. *(UNALIGNED WORD *) &pwireRecord->DataLength = htons( wDataLength );
  657. return( pchBuf + sizeof(DNS_WIRE_RECORD) );
  658. }
  659. VOID
  660. Dns_SetRecordDatalength(
  661. IN OUT PDNS_WIRE_RECORD pRecord,
  662. IN WORD wDataLength
  663. )
  664. /*++
  665. Routine Description:
  666. Reset record datalength.
  667. Arguments:
  668. pRecord - wire record to reset
  669. wDataLength - data length
  670. Return Value:
  671. Ptr to data section of record.
  672. --*/
  673. {
  674. WORD flippedWord;
  675. INLINE_WORD_FLIP( flippedWord, wDataLength );
  676. *(UNALIGNED WORD *) &pRecord->DataLength = flippedWord;
  677. }
  678. DNS_STATUS
  679. Dns_AddRecordsToMessage(
  680. IN OUT PDNS_MSG_BUF pMsg,
  681. IN PDNS_RECORD pRecord,
  682. IN BOOL fUpdateMessage
  683. )
  684. /*++
  685. Routine Description:
  686. Add record or list of records to message.No data RR cases:
  687. This includes prereqs and deletes except for specific record cases.
  688. Arguments:
  689. pMsg - message buffer to write to
  690. pRecord - ptr to record (or first of list of records) to write to packet
  691. fUpdateMessage -- If TRUE, the message is going to contain an update.
  692. Therefore the section flags in the pRecord
  693. should be interpreted for update. Otherwise this is
  694. for a query message and section flags should be
  695. interpreted for query.
  696. Return Value:
  697. ERROR_SUCCESS if successful.
  698. Error code on failure.
  699. --*/
  700. {
  701. PCHAR pch = pMsg->pCurrent;
  702. PCHAR pendBuffer = pMsg->pBufferEnd;
  703. WORD currentSection = 0;
  704. WORD section;
  705. PDNS_NAME pnamePrevious = NULL;
  706. WORD compressedPreviousName;
  707. WORD offsetPreviousName;
  708. PDNS_WIRE_RECORD pwireRecord;
  709. PCHAR pchnext;
  710. WORD index;
  711. DNS_STATUS status = ERROR_SUCCESS;
  712. //
  713. // write each record in list
  714. //
  715. while ( pRecord )
  716. {
  717. //
  718. // determine section for record
  719. // - may not write to previous section
  720. section = (WORD) pRecord->Flags.S.Section;
  721. if ( section < currentSection )
  722. {
  723. DNS_PRINT((
  724. "ERROR: Attempt to write record at %p, with section %d\n"
  725. "\tless than previous section written %d.\n",
  726. pRecord,
  727. pRecord->Flags.S.Section,
  728. currentSection ));
  729. return( ERROR_INVALID_DATA );
  730. }
  731. else if ( section > currentSection )
  732. {
  733. currentSection = section;
  734. SET_CURRENT_RR_COUNT_SECTION( pMsg, section );
  735. }
  736. //
  737. // write record name
  738. // - if same as previous, write compressed name
  739. // - if first write from pRecord
  740. // - write full name
  741. // - clear reserved field for offsetting
  742. //
  743. if ( pnamePrevious && !strcmp( pnamePrevious, pRecord->pName ) )
  744. {
  745. // compression should always be BACK ptr
  746. DNS_ASSERT( offsetPreviousName < pch - (PCHAR)&pMsg->MessageHead );
  747. if ( pendBuffer <= pch + sizeof(WORD) )
  748. {
  749. return( ERROR_MORE_DATA );
  750. }
  751. if ( ! compressedPreviousName )
  752. {
  753. compressedPreviousName =
  754. htons( (WORD)(0xC000 | offsetPreviousName) );
  755. }
  756. *(UNALIGNED WORD *)pch = compressedPreviousName;
  757. pch += sizeof( WORD );
  758. }
  759. else
  760. {
  761. offsetPreviousName = (WORD)(pch - (PCHAR)&pMsg->MessageHead);
  762. compressedPreviousName = 0;
  763. pnamePrevious = pRecord->pName;
  764. pch = Dns_WriteDottedNameToPacket(
  765. pch,
  766. pendBuffer,
  767. pnamePrevious,
  768. NULL,
  769. 0,
  770. ( RECORD_CHARSET(pRecord) == DnsCharSetUnicode ) );
  771. if ( !pch )
  772. {
  773. // DCR: distinguish out of space errors from name errors during write
  774. return( DNS_ERROR_INVALID_NAME );
  775. }
  776. }
  777. //
  778. // write record structure
  779. //
  780. if ( pch + sizeof(DNS_WIRE_RECORD) >= pendBuffer )
  781. {
  782. return( ERROR_MORE_DATA );
  783. }
  784. status = Dns_WriteRecordStructureToPacket(
  785. pch,
  786. pRecord,
  787. fUpdateMessage );
  788. if ( status != ERROR_SUCCESS )
  789. {
  790. return( status );
  791. }
  792. pwireRecord = (PDNS_WIRE_RECORD) pch;
  793. pch += sizeof( DNS_WIRE_RECORD );
  794. //
  795. // record data
  796. //
  797. if ( pRecord->wDataLength )
  798. {
  799. index = INDEX_FOR_TYPE( pRecord->wType );
  800. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  801. if ( index && RRWriteTable[ index ] )
  802. {
  803. pchnext = RRWriteTable[ index ](
  804. pRecord,
  805. pch,
  806. pendBuffer
  807. );
  808. if ( ! pchnext )
  809. {
  810. status = GetLastError();
  811. DNS_PRINT((
  812. "ERROR: Record write routine failure for record type %d.\n\n"
  813. "\tstatus = %p\n",
  814. pRecord->wType,
  815. status ));
  816. return( status );
  817. }
  818. }
  819. else
  820. {
  821. // write unknown types -- as RAW data only
  822. DNS_PRINT((
  823. "WARNING: Writing unknown type %d to message\n",
  824. pRecord->wType ));
  825. if ( pendBuffer - pch <= pRecord->wDataLength )
  826. {
  827. return( ERROR_MORE_DATA );
  828. }
  829. memcpy(
  830. pch,
  831. (PCHAR) &pRecord->Data,
  832. pRecord->wDataLength );
  833. pchnext = pch + pRecord->wDataLength;
  834. }
  835. //
  836. // set packet record data length
  837. //
  838. DNS_ASSERT( (pchnext - pch) < MAXWORD );
  839. *(UNALIGNED WORD *) &pwireRecord->DataLength =
  840. htons( (WORD)(pchnext - pch) );
  841. pch = pchnext;
  842. }
  843. // increment message record count
  844. CURRENT_RR_COUNT_FIELD(pMsg)++;
  845. pRecord = pRecord->pNext;
  846. }
  847. //
  848. // resest message current ptr
  849. //
  850. pMsg->pCurrent = pch;
  851. IF_DNSDBG( INIT )
  852. {
  853. DnsDbg_Message(
  854. "Packet after adding records:",
  855. pMsg );
  856. }
  857. return( status );
  858. }
  859. //
  860. // Reading from packet
  861. //
  862. PCHAR
  863. _fastcall
  864. Dns_SkipPacketName(
  865. IN PCHAR pch,
  866. IN PCHAR pchEnd
  867. )
  868. /*++
  869. Routine Description:
  870. Skips over name in packet
  871. Arguments:
  872. pch - ptr to start of name to skip
  873. pchEnd - ptr to byte after end of packet
  874. Return Value:
  875. Ptr to next byte in buffer
  876. NULL if bad name
  877. --*/
  878. {
  879. register UCHAR cch;
  880. register UCHAR cflag;
  881. //
  882. // Loop until end of name
  883. //
  884. while ( pch < pchEnd )
  885. {
  886. cch = *pch++;
  887. cflag = cch & 0xC0;
  888. //
  889. // normal label
  890. // - skip to next label and continue
  891. // - stop only if at 0 (root) label
  892. //
  893. if ( cflag == 0 )
  894. {
  895. if ( cch )
  896. {
  897. pch += cch;
  898. continue;
  899. }
  900. return( pch );
  901. }
  902. //
  903. // compression
  904. // - skip second byte in compression and return
  905. //
  906. else if ( cflag == 0xC0 )
  907. {
  908. pch++;
  909. return( pch );
  910. }
  911. else
  912. {
  913. DNSDBG( READ, (
  914. "ERROR: bad packet name label byte %02 at 0x%p\n",
  915. cch,
  916. pch - 1
  917. ));
  918. return( NULL );
  919. }
  920. }
  921. DNSDBG( READ, (
  922. "ERROR: packet name at %p reads past end of packet at %p\n",
  923. pch,
  924. pchEnd
  925. ));
  926. return( NULL );
  927. }
  928. PCHAR
  929. _fastcall
  930. Dns_SkipPacketRecord(
  931. IN PCHAR pchRecord,
  932. IN PCHAR pchMsgEnd
  933. )
  934. /*++
  935. Routine Description:
  936. Skips over packet RR.
  937. This is RR structure and data, not the owner name.
  938. Arguments:
  939. pchRecord - ptr to start of RR structure.
  940. pchMsgEnd - end of message
  941. Return Value:
  942. Ptr to next record in packet.
  943. NULL if RR outside packet or invalid.
  944. --*/
  945. {
  946. //
  947. // skip RR struct
  948. //
  949. pchRecord += sizeof(DNS_WIRE_RECORD);
  950. if ( pchRecord > pchMsgEnd )
  951. {
  952. return( NULL );
  953. }
  954. // read datalength and skip data
  955. // datalength field is a WORD, at end of record header
  956. pchRecord += InlineFlipUnalignedWord( pchRecord - sizeof(WORD) );
  957. if ( pchRecord > pchMsgEnd )
  958. {
  959. return( NULL );
  960. }
  961. return( pchRecord );
  962. }
  963. PCHAR
  964. Dns_SkipToRecord(
  965. IN PDNS_HEADER pMsgHead,
  966. IN PCHAR pMsgEnd,
  967. IN INT iCount
  968. )
  969. /*++
  970. Routine Description:
  971. Skips over some number of DNS records.
  972. Arguments:
  973. pMsgHead -- ptr to begining of DNS message.
  974. pMsgEnd -- ptr to end of DNS message
  975. iCount -- if > 0, number of records to skip
  976. if <= 0, number of records from end of packet
  977. Return Value:
  978. Ptr to next
  979. NULL if bad packet.
  980. --*/
  981. {
  982. PCHAR pch;
  983. INT i;
  984. WORD recordCount;
  985. //
  986. // determine how many records to skip
  987. //
  988. recordCount = pMsgHead->QuestionCount
  989. + pMsgHead->AnswerCount
  990. + pMsgHead->NameServerCount
  991. + pMsgHead->AdditionalCount;
  992. // iCount > 0 is skip count, MUST not be larger than
  993. // actual count
  994. if ( iCount > 0 )
  995. {
  996. if ( iCount > recordCount )
  997. {
  998. return( NULL );
  999. }
  1000. }
  1001. // iCount <= 0 then (-iCount) is number of records
  1002. // from the last record
  1003. else
  1004. {
  1005. iCount += recordCount;
  1006. if ( iCount < 0 )
  1007. {
  1008. return( NULL );
  1009. }
  1010. }
  1011. // skip message header
  1012. pch = (PCHAR) (pMsgHead + 1);
  1013. if ( iCount == 0 )
  1014. {
  1015. return( pch );
  1016. }
  1017. // skip the question/zone section
  1018. pch = Dns_SkipPacketName( pch, pMsgEnd );
  1019. if ( !pch )
  1020. {
  1021. return pch;
  1022. }
  1023. pch += sizeof(DNS_WIRE_QUESTION);
  1024. //
  1025. // skip regular records
  1026. //
  1027. iCount--;
  1028. for ( i=0; i<iCount; i++ )
  1029. {
  1030. pch = Dns_SkipPacketName( pch, pMsgEnd );
  1031. if ( !pch )
  1032. {
  1033. return pch;
  1034. }
  1035. pch = Dns_SkipPacketRecord( pch, pMsgEnd );
  1036. if ( !pch )
  1037. {
  1038. return pch;
  1039. }
  1040. }
  1041. DNSDBG( READ, (
  1042. "Leaving SkipToRecord, current ptr = %p, offset = %04x\n"
  1043. "\tskipped %d records\n",
  1044. pch,
  1045. (WORD) (pch - (PCHAR)pMsgHead),
  1046. iCount+1 ));
  1047. return( pch );
  1048. }
  1049. PCHAR
  1050. _fastcall
  1051. Dns_ReadPacketName(
  1052. IN OUT PCHAR pchNameBuffer,
  1053. OUT PWORD pwNameLength,
  1054. IN OUT PWORD pwNameOffset, OPTIONAL
  1055. OUT PBOOL pfNameSameAsPrevious, OPTIONAL
  1056. IN PCHAR pchName,
  1057. IN PCHAR pchStart,
  1058. IN PCHAR pchEnd
  1059. )
  1060. /*++
  1061. Routine Description:
  1062. Reads packet name and converts it to DNS-dotted format.
  1063. Arguments:
  1064. pchNameBuffer - buffer to write name into; contains previous name, if any
  1065. pwNameLength - length of name written to buffer; does not include
  1066. terminating NULL
  1067. pwNameOffset - addr with previous names offset (zero if no previous name);
  1068. on return, contains this name's offset
  1069. OPTIONAL but must exist if fNameSameAsPrevious exists
  1070. pfNameSameAsPrevious - addr of flag set if this name is same as previous;
  1071. OPTIONAL but must exist if pwNameOffset exists
  1072. pchName - ptr to name in packet
  1073. pchStart - start of DNS message
  1074. pchEnd - ptr to byte after end of DNS message
  1075. Return Value:
  1076. Ptr to next byte in packet.
  1077. NULL on error.
  1078. --*/
  1079. {
  1080. register PUCHAR pch = pchName;
  1081. register UCHAR cch;
  1082. register UCHAR cflag;
  1083. PCHAR pchdotted;
  1084. PCHAR pbufferEnd;
  1085. PCHAR pchreturn = NULL;
  1086. DNS_ASSERT( pch > pchStart && pchEnd > pchStart );
  1087. DNS_ASSERT( (pwNameOffset && pfNameSameAsPrevious) ||
  1088. (!pwNameOffset && !pfNameSameAsPrevious) );
  1089. //
  1090. // read through labels and/or compression until reach end of name
  1091. //
  1092. pbufferEnd = pchNameBuffer + DNS_MAX_NAME_LENGTH;
  1093. pchdotted = pchNameBuffer;
  1094. while ( pch < pchEnd )
  1095. {
  1096. cch = *pch++;
  1097. //
  1098. // at root label
  1099. // - if root name, write single '.'
  1100. // - otherwise strip trailing dot from last label
  1101. // - save length written
  1102. // - NULL teminate name
  1103. // - set same as previous FALSE
  1104. // - save packet offset to this name
  1105. // - return next byte in buffer
  1106. //
  1107. if ( cch == 0 )
  1108. {
  1109. if ( pchdotted == pchNameBuffer )
  1110. {
  1111. *pchdotted++ = '.';
  1112. }
  1113. else
  1114. {
  1115. pchdotted--;
  1116. }
  1117. *pwNameLength = (WORD)(pchdotted - pchNameBuffer);
  1118. *pchdotted = 0;
  1119. if ( pwNameOffset )
  1120. {
  1121. *pfNameSameAsPrevious = FALSE;
  1122. *pwNameOffset = (WORD)(pchName - pchStart);
  1123. }
  1124. return( pchreturn ? pchreturn : pch );
  1125. }
  1126. cflag = cch & 0xC0;
  1127. //
  1128. // regular label
  1129. // - copy label to buffer
  1130. // - jump to next label
  1131. if ( cflag == 0 )
  1132. {
  1133. PCHAR pchnext = pch + cch;
  1134. if ( pchnext >= pchEnd )
  1135. {
  1136. DNS_PRINT((
  1137. "ERROR: Packet name at %p extends past end of buffer\n",
  1138. pchName ));
  1139. goto Failed;
  1140. }
  1141. if ( pchdotted + cch + 1 >= pbufferEnd )
  1142. {
  1143. DNS_PRINT((
  1144. "ERROR: Packet name at %p exceeds max length\n",
  1145. pchName ));
  1146. goto Failed;
  1147. }
  1148. memcpy(
  1149. pchdotted,
  1150. pch,
  1151. cch );
  1152. pchdotted += cch;
  1153. *pchdotted++ = '.';
  1154. pch = pchnext;
  1155. continue;
  1156. }
  1157. //
  1158. // offset
  1159. // - get offset
  1160. // - if offset at start of name compare to previous name offset
  1161. // - otherwise follow offset to build new name
  1162. //
  1163. if ( cflag == 0xC0 )
  1164. {
  1165. WORD offset;
  1166. PCHAR pchoffset;
  1167. offset = (cch ^ 0xC0);
  1168. offset <<= 8;
  1169. offset |= *pch;
  1170. pchoffset = --pch;
  1171. //
  1172. // first offset
  1173. // - save return pointer
  1174. //
  1175. // if name is entirely offset
  1176. // - same as previous offset -- done
  1177. // - if not still save this offset rather than offset
  1178. // to name itself (first answer is usually just offset
  1179. // to question, subsequent answer RRs continue to reference
  1180. // question offset, not first answer)
  1181. //
  1182. if ( !pchreturn )
  1183. {
  1184. DNS_ASSERT( pch >= pchName );
  1185. pchreturn = pch+2;
  1186. if ( pchoffset == pchName && pwNameOffset )
  1187. {
  1188. if ( *pwNameOffset == offset )
  1189. {
  1190. *pfNameSameAsPrevious = TRUE;
  1191. return( pchreturn );
  1192. }
  1193. else
  1194. {
  1195. // save offset that comprises name
  1196. // then kill out copy of return ptr so don't
  1197. // return offset to pchName when finish copy
  1198. *pwNameOffset = offset;
  1199. *pfNameSameAsPrevious = FALSE;
  1200. pwNameOffset = NULL;
  1201. }
  1202. }
  1203. }
  1204. //
  1205. // make jump to new bytes and continue
  1206. // - verify offset is BEFORE current name
  1207. // and BEFORE current ptr
  1208. pch = pchStart + offset;
  1209. if ( pch >= pchName || pch >= pchoffset )
  1210. {
  1211. DNS_PRINT((
  1212. "ERROR: Bogus name offset %d, encountered at %p\n"
  1213. "\tto location %p past current position or original name.\n",
  1214. offset,
  1215. pchoffset,
  1216. pch ));
  1217. goto Failed;
  1218. }
  1219. continue;
  1220. }
  1221. // any other label byte is bogus
  1222. else
  1223. {
  1224. DNS_PRINT((
  1225. "ERROR: bogus name label byte %02x at %p\n",
  1226. cch,
  1227. pch - 1 ));
  1228. goto Failed;
  1229. }
  1230. }
  1231. DNS_PRINT((
  1232. "ERROR: packet name at %p reads to ptr %p past end of packet at %p\n",
  1233. pchName,
  1234. pch,
  1235. pchEnd ));
  1236. //
  1237. // failed
  1238. // - return NULL
  1239. // - set OUT params, keeps prefix happy on higher level calls
  1240. //
  1241. Failed:
  1242. *pwNameLength = 0;
  1243. if ( pwNameOffset )
  1244. {
  1245. *pwNameOffset = 0;
  1246. }
  1247. if ( pfNameSameAsPrevious )
  1248. {
  1249. *pfNameSameAsPrevious = FALSE;
  1250. }
  1251. return ( NULL );
  1252. }
  1253. PCHAR
  1254. _fastcall
  1255. Dns_ReadPacketNameAllocate(
  1256. IN OUT PCHAR * ppchName,
  1257. OUT PWORD pwNameLength,
  1258. IN OUT PWORD pwPrevNameOffset, OPTIONAL
  1259. OUT PBOOL pfNameSameAsPrevious, OPTIONAL
  1260. IN PCHAR pchPacketName,
  1261. IN PCHAR pchStart,
  1262. IN PCHAR pchEnd
  1263. )
  1264. /*++
  1265. Routine Description:
  1266. Reads packet name and creates (allocates) a copy in DNS-dotted format.
  1267. Arguments:
  1268. ppchName - addr to recv resulting name ptr
  1269. pwNameLength - length of name written to buffer
  1270. pwNameOffset - addr with previous names offset (zero if no previous name);
  1271. on return, contains this name's offset
  1272. OPTIONAL but must exist if fNameSameAsPrevious exists
  1273. fNameSameAsPrevious - addr of flag set if this name is same as previous;
  1274. OPTIONAL but must exist if pwNameOffset exists
  1275. pchPacketName - pch to name in packet
  1276. pchStart - start of DNS message
  1277. pchEnd - ptr to byte after end of DNS message
  1278. Return Value:
  1279. Ptr to next byte in packet.
  1280. NULL on error.
  1281. --*/
  1282. {
  1283. PCHAR pch;
  1284. PCHAR pallocName;
  1285. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1286. WORD nameLength = DNS_MAX_NAME_BUFFER_LENGTH;
  1287. //
  1288. // read packet name into buffer
  1289. //
  1290. pch = Dns_ReadPacketName(
  1291. nameBuffer,
  1292. & nameLength,
  1293. pwPrevNameOffset,
  1294. pfNameSameAsPrevious,
  1295. pchPacketName,
  1296. pchStart,
  1297. pchEnd );
  1298. if ( !pch )
  1299. {
  1300. return( pch );
  1301. }
  1302. //
  1303. // allocate buffer for packet name
  1304. // - nameLength does not include terminating NULL
  1305. //
  1306. nameLength++;
  1307. pallocName = (PCHAR) ALLOCATE_HEAP( nameLength );
  1308. if ( !pallocName )
  1309. {
  1310. return( NULL );
  1311. }
  1312. RtlCopyMemory(
  1313. pallocName,
  1314. nameBuffer,
  1315. nameLength );
  1316. *ppchName = pallocName;
  1317. *pwNameLength = --nameLength;
  1318. DNSDBG( READ, (
  1319. "Allocated copy of packet name %s length %d\n",
  1320. pallocName,
  1321. nameLength ));
  1322. return( pch );
  1323. }
  1324. DNS_STATUS
  1325. Dns_ExtractRecordsFromMessage(
  1326. IN PDNS_MSG_BUF pMsg,
  1327. IN BOOL fUnicode,
  1328. OUT PDNS_RECORD * ppRecord
  1329. )
  1330. /*++
  1331. Routine Description:
  1332. Extract records from packet.
  1333. Arguments:
  1334. pMsg - message buffer to write to
  1335. fUnicode - flag indicating strings in record should be unicode
  1336. Return Value:
  1337. Ptr to parsed record list if any.
  1338. NULL if no record list or error.
  1339. --*/
  1340. {
  1341. PDNS_MESSAGE_BUFFER pDnsBuffer = (PDNS_MESSAGE_BUFFER) &pMsg->MessageHead;
  1342. return Dns_ExtractRecordsFromBuffer(
  1343. pDnsBuffer,
  1344. pMsg->MessageLength,
  1345. fUnicode,
  1346. ppRecord );
  1347. }
  1348. DNS_STATUS
  1349. Dns_ExtractRecordsFromBuffer(
  1350. IN PDNS_MESSAGE_BUFFER pDnsBuffer,
  1351. IN WORD wMessageLength,
  1352. IN BOOL fUnicode,
  1353. OUT PDNS_RECORD * ppRecord
  1354. )
  1355. /*++
  1356. Routine Description:
  1357. Extract records from packet buffer.
  1358. Arguments:
  1359. pDnsBuffer - message buffer to read from
  1360. fUnicode - flag indicating strings in record should be unicode
  1361. Return Value:
  1362. Ptr to parsed record list if any.
  1363. NULL if no record list or error.
  1364. --*/
  1365. {
  1366. register PCHAR pch;
  1367. PDNS_HEADER pwireMsg = (PDNS_HEADER) pDnsBuffer;
  1368. PCHAR pchpacketEnd;
  1369. DNS_PARSED_RR parsedRR;
  1370. LPSTR pnameOwner;
  1371. LPSTR pnameNew = NULL;
  1372. DNS_CHARSET outCharSet;
  1373. WORD countRR;
  1374. WORD countSection;
  1375. WORD typePrevious = 0;
  1376. WORD nameOffset = 0;
  1377. WORD nameLength;
  1378. WORD index;
  1379. BYTE section;
  1380. BOOL fnameSameAsPrevious;
  1381. PDNS_RECORD pnewRR;
  1382. DNS_RRSET rrset;
  1383. DNS_STATUS status;
  1384. DNS_STATUS rcodeStatus;
  1385. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1386. DNS_RECORD recordTemp;
  1387. PWORD pCurrentCountField = NULL;
  1388. DNSDBG( READ, (
  1389. "Dns_ExtractRecordsFromBuffer( %p, len=%d )\n",
  1390. pDnsBuffer,
  1391. wMessageLength
  1392. ));
  1393. //
  1394. // error code
  1395. // - map RCODE to DNS error
  1396. // - if other than NAME_ERROR, don't bother parsing
  1397. //
  1398. rcodeStatus = pwireMsg->ResponseCode;
  1399. if ( rcodeStatus != 0 )
  1400. {
  1401. rcodeStatus = Dns_MapRcodeToStatus( pwireMsg->ResponseCode );
  1402. if ( rcodeStatus != DNS_ERROR_RCODE_NAME_ERROR )
  1403. {
  1404. DNSDBG( READ, (
  1405. "No records extracted from response\n"
  1406. "\tresponse code = %d\n",
  1407. pwireMsg->ResponseCode ));
  1408. return( rcodeStatus );
  1409. }
  1410. }
  1411. DNS_RRSET_INIT( rrset );
  1412. //
  1413. // detemine char set
  1414. //
  1415. if ( fUnicode )
  1416. {
  1417. outCharSet = DnsCharSetUnicode;
  1418. }
  1419. else
  1420. {
  1421. outCharSet = DnsCharSetUtf8;
  1422. }
  1423. //
  1424. // read RRs in list of records
  1425. //
  1426. // loop through all resource records
  1427. // - skip question
  1428. // - build DNS_RECORD structure for other records
  1429. //
  1430. pchpacketEnd = (PCHAR)pwireMsg + wMessageLength;
  1431. pch = pDnsBuffer->MessageBody;
  1432. section = DNSREC_QUESTION;
  1433. pCurrentCountField = &pwireMsg->QuestionCount;
  1434. countSection = pwireMsg->QuestionCount;
  1435. countRR = 0;
  1436. while( 1 )
  1437. {
  1438. //
  1439. // changing sections
  1440. // save section number and RR count for current section
  1441. // note need immediate loop back to handle empty section
  1442. //
  1443. countRR++;
  1444. if ( countRR > countSection )
  1445. {
  1446. if ( section == DNSREC_ADDITIONAL )
  1447. {
  1448. break;
  1449. }
  1450. section++;
  1451. pCurrentCountField++;
  1452. countSection = *(pCurrentCountField);
  1453. countRR = 0;
  1454. continue;
  1455. }
  1456. // validity check next RR
  1457. if ( pch >= pchpacketEnd )
  1458. {
  1459. DNS_PRINT((
  1460. "ERROR: reading bad packet %p.\n"
  1461. "\tat end of packet length with more records to process\n"
  1462. "\tpacket length = %ld\n"
  1463. "\tcurrent offset = %ld\n",
  1464. wMessageLength,
  1465. pch - (PCHAR)pwireMsg
  1466. ));
  1467. goto PacketError;
  1468. }
  1469. //
  1470. // skip question
  1471. //
  1472. if ( section == DNSREC_QUESTION )
  1473. {
  1474. pch = Dns_SkipPacketName(
  1475. pch,
  1476. pchpacketEnd );
  1477. if ( !pch )
  1478. {
  1479. DNS_PRINT(( "ERROR: bad question name.\n" ));
  1480. goto PacketError;
  1481. }
  1482. pch += sizeof(DNS_WIRE_QUESTION);
  1483. if ( pch > pchpacketEnd )
  1484. {
  1485. DNS_PRINT(( "ERROR: question exceeds packet length.\n" ));
  1486. goto PacketError;
  1487. }
  1488. continue;
  1489. }
  1490. //
  1491. // read name, determining if same as previous name
  1492. //
  1493. IF_DNSDBG( READ2 )
  1494. {
  1495. DnsDbg_Lock();
  1496. DNS_PRINT((
  1497. "Reading record at offset %x\n",
  1498. (WORD)(pch - (PCHAR)pwireMsg) ));
  1499. DnsDbg_PacketName(
  1500. "Record name ",
  1501. pch,
  1502. pwireMsg,
  1503. pchpacketEnd,
  1504. "\n" );
  1505. DnsDbg_Unlock();
  1506. }
  1507. pch = Dns_ReadPacketName(
  1508. nameBuffer,
  1509. & nameLength,
  1510. & nameOffset,
  1511. & fnameSameAsPrevious,
  1512. pch,
  1513. (PCHAR) pwireMsg,
  1514. pchpacketEnd );
  1515. if ( ! pch )
  1516. {
  1517. DNS_PRINT(( "ERROR: bad packet name.\n" ));
  1518. goto PacketError;
  1519. }
  1520. IF_DNSDBG( READ2 )
  1521. {
  1522. DNS_PRINT((
  1523. "Owner name of record %s\n"
  1524. "\tlength = %d\n"
  1525. "\toffset = %d\n"
  1526. "\tfSameAsPrevious = %d\n",
  1527. nameBuffer,
  1528. nameLength,
  1529. nameOffset,
  1530. fnameSameAsPrevious ));
  1531. }
  1532. //
  1533. // extract RR info, type, datalength
  1534. // - verify RR within message
  1535. //
  1536. pch = Dns_ReadRecordStructureFromPacket(
  1537. pch,
  1538. pchpacketEnd,
  1539. & parsedRR );
  1540. if ( !pch )
  1541. {
  1542. DNS_PRINT(( "ERROR: bad RR struct out of packet.\n" ));
  1543. goto PacketError;
  1544. }
  1545. //
  1546. // on type change, always have new RR set
  1547. // - setup for new name
  1548. //
  1549. if ( parsedRR.Type != typePrevious )
  1550. {
  1551. fnameSameAsPrevious = FALSE;
  1552. typePrevious = parsedRR.Type;
  1553. }
  1554. //
  1555. // make copy of new name
  1556. //
  1557. // DCR_FIX0: name same as previous
  1558. // flag indicates only that name not compressed to previous
  1559. // name (or previous compression)
  1560. // actually need abolute ingnore case compare
  1561. // with last records name to be sure that name not previous
  1562. //
  1563. if ( !fnameSameAsPrevious )
  1564. {
  1565. pnameNew = Dns_NameCopyAllocate(
  1566. nameBuffer,
  1567. (UCHAR) nameLength,
  1568. DnsCharSetUtf8, // UTF8 string in
  1569. outCharSet
  1570. );
  1571. pnameOwner = pnameNew;
  1572. DNSDBG( READ2, (
  1573. "Copy of owner name of record being read from packet %s\n",
  1574. pnameOwner ));
  1575. }
  1576. DNS_ASSERT( pnameOwner );
  1577. DNS_ASSERT( pnameNew || fnameSameAsPrevious );
  1578. //
  1579. // TSIG record requires owner name for versioning
  1580. //
  1581. recordTemp.pName = pnameOwner;
  1582. //
  1583. // read RR data for type
  1584. //
  1585. index = INDEX_FOR_TYPE( parsedRR.Type );
  1586. DNS_ASSERT( index <= MAX_RECORD_TYPE_INDEX );
  1587. if ( !index || !RRReadTable[ index ] )
  1588. {
  1589. // unknown types -- index to NULL type to use
  1590. // FlatRecordRead()
  1591. DNS_PRINT((
  1592. "WARNING: Reading unknown record type %d from message\n",
  1593. parsedRR.Type ));
  1594. index = DNS_TYPE_NULL;
  1595. }
  1596. pnewRR = RRReadTable[ index ](
  1597. &recordTemp,
  1598. outCharSet,
  1599. (PCHAR) pDnsBuffer,
  1600. parsedRR.pchData,
  1601. pch // end of record data
  1602. );
  1603. if ( ! pnewRR )
  1604. {
  1605. status = GetLastError();
  1606. ASSERT( status != ERROR_SUCCESS );
  1607. DNS_PRINT((
  1608. "ERROR: RRReadTable routine failure for type %d.\n"
  1609. "\tstatus = %p\n"
  1610. "\tpchdata = %p\n"
  1611. "\tpchend = %p\n",
  1612. parsedRR.Type,
  1613. status,
  1614. parsedRR.pchData,
  1615. pch ));
  1616. if ( status == ERROR_SUCCESS )
  1617. {
  1618. status = DNS_ERROR_NO_MEMORY;
  1619. }
  1620. goto Failed;
  1621. }
  1622. //
  1623. // write record info
  1624. // - first RR in set gets new name allocation
  1625. // and is responsible for cleanup
  1626. // - no data cleanup necessary as all data is
  1627. // contained in the RR allocation
  1628. //
  1629. pnewRR->pName = pnameOwner;
  1630. pnewRR->wType = parsedRR.Type;
  1631. pnewRR->dwTtl = parsedRR.Ttl;
  1632. pnewRR->Flags.S.Section = section;
  1633. pnewRR->Flags.S.CharSet = outCharSet;
  1634. FLAG_FreeOwner( pnewRR ) = !fnameSameAsPrevious;
  1635. FLAG_FreeData( pnewRR ) = FALSE;
  1636. // add RR to list
  1637. DNS_RRSET_ADD( rrset, pnewRR );
  1638. // clear new ptr, as name now part of record
  1639. // this is strictly used to determine when pnameOwner
  1640. // must be cleaned up on failure
  1641. pnameNew = NULL;
  1642. } // end loop through packet's records
  1643. //
  1644. // return parsed record list
  1645. // - return DNS error for RCODE
  1646. // - set special return code to differentiate empty response
  1647. //
  1648. // DCR: should have special REFERRAL response
  1649. // - could overload NOTAUTH rcode
  1650. // DCR: should have special EMPTY_AUTH response
  1651. // - could have empty-auth overload NXRRSET
  1652. //
  1653. // DCR: best check on distinguishing EMPTY_AUTH from REFERRAL
  1654. //
  1655. if ( pwireMsg->AnswerCount == 0 && rcodeStatus == 0 )
  1656. {
  1657. if ( !rrset.pFirstRR || rrset.pFirstRR->wType == DNS_TYPE_SOA )
  1658. {
  1659. rcodeStatus = DNS_INFO_NO_RECORDS;
  1660. DNSDBG( READ, ( "Empty-auth response at %p.\n", pwireMsg ));
  1661. }
  1662. #if 0
  1663. else if ( rrset.pFirstRR->wType == DNS_TYPE_NS &&
  1664. !pwireMsg->Authoritative )
  1665. {
  1666. rcodeStatus = DNS_INFO_REFERRAL;
  1667. DNSDBG( READ, ( "Referral response at %p.\n", pwireMsg ));
  1668. }
  1669. else
  1670. {
  1671. rcodeStatus = DNS_ERROR_BAD_PACKET;
  1672. DNSDBG( READ, ( "Bogus NO_ERROR response at %p.\n", pwireMsg ));
  1673. }
  1674. #endif
  1675. }
  1676. // verify never turn RCODE result into SUCCESS
  1677. ASSERT( pwireMsg->ResponseCode == 0 || rcodeStatus != ERROR_SUCCESS );
  1678. ASSERT( pnameNew == NULL );
  1679. *ppRecord = rrset.pFirstRR;
  1680. IF_DNSDBG( RECV )
  1681. {
  1682. DnsDbg_RecordSet(
  1683. "Extracted records:\n",
  1684. *ppRecord );
  1685. }
  1686. return( rcodeStatus );
  1687. PacketError:
  1688. DNS_PRINT(( "ERROR: bad packet in buffer.\n" ));
  1689. status = DNS_ERROR_BAD_PACKET;
  1690. Failed:
  1691. if ( pnameNew )
  1692. {
  1693. FREE_HEAP( pnameNew );
  1694. }
  1695. Dns_RecordListFree(
  1696. rrset.pFirstRR,
  1697. TRUE // free owner names also
  1698. );
  1699. return( status );
  1700. }
  1701. void
  1702. Dns_NormalizeAllRecordTtls(
  1703. IN PDNS_RECORD pRecord
  1704. )
  1705. /*++
  1706. Routine Description:
  1707. Finds the lowest Ttl value in RR set and the sets all
  1708. records to that value.
  1709. Arguments:
  1710. pRecord - record set to normalize ttl values of.
  1711. Return Value:
  1712. None
  1713. --*/
  1714. {
  1715. PDNS_RECORD pTemp = pRecord;
  1716. DWORD dwTtl;
  1717. WORD wType;
  1718. //
  1719. // Get the Ttl of the first record (if there is one)
  1720. //
  1721. if ( pTemp )
  1722. {
  1723. dwTtl = pTemp->dwTtl;
  1724. wType = pTemp->wType;
  1725. pTemp = pTemp->pNext;
  1726. }
  1727. //
  1728. // Walk any remaining records looking for an even lower ttl value
  1729. //
  1730. while ( pTemp &&
  1731. pTemp->wType == wType &&
  1732. pTemp->Flags.S.Section == DNSREC_ANSWER )
  1733. {
  1734. if ( pTemp->dwTtl < dwTtl )
  1735. {
  1736. dwTtl = pTemp->dwTtl;
  1737. }
  1738. pTemp = pTemp->pNext;
  1739. }
  1740. //
  1741. // Set all records to this lowest ttl value
  1742. //
  1743. pTemp = pRecord;
  1744. while ( pTemp &&
  1745. pTemp->wType == wType &&
  1746. pTemp->Flags.S.Section == DNSREC_ANSWER )
  1747. {
  1748. pTemp->dwTtl = dwTtl;
  1749. pTemp = pTemp->pNext;
  1750. }
  1751. }
  1752. PCHAR
  1753. Dns_ReadRecordStructureFromPacket(
  1754. IN PCHAR pchPacket,
  1755. IN PCHAR pchMsgEnd,
  1756. IN OUT PDNS_PARSED_RR pParsedRR
  1757. )
  1758. /*++
  1759. Routine Description:
  1760. Read record structure from packet.
  1761. Arguments:
  1762. pchPacket - ptr to record structure in packet
  1763. pRecord - ptr to record structure to receive record structure
  1764. with fields in host order (and aligned).
  1765. Return Value:
  1766. Ptr to next record in packet -- based on datalength.
  1767. Zero on error.
  1768. --*/
  1769. {
  1770. DNSDBG( READ2, (
  1771. "Dns_ReadRecordStructureFromPacket(%p).\n",
  1772. pchPacket ));
  1773. //
  1774. // verify record structure within packet
  1775. //
  1776. if ( pchPacket + sizeof(DNS_WIRE_RECORD) > pchMsgEnd )
  1777. {
  1778. DNS_PRINT((
  1779. "ERROR: record structure at %p is not within packet!.\n",
  1780. pchPacket ));
  1781. return( 0 );
  1782. }
  1783. //
  1784. // flip fields and write to aligned struct
  1785. //
  1786. pParsedRR->pchRR = pchPacket;
  1787. pParsedRR->Type = InlineFlipUnalignedWord( pchPacket );
  1788. pchPacket += sizeof(WORD);
  1789. pParsedRR->Class = InlineFlipUnalignedWord( pchPacket );
  1790. pchPacket += sizeof(WORD);
  1791. pParsedRR->Ttl = InlineFlipUnalignedDword( pchPacket );
  1792. pchPacket += sizeof(DWORD);
  1793. pParsedRR->DataLength = InlineFlipUnalignedWord( pchPacket );
  1794. pchPacket += sizeof(WORD);
  1795. pParsedRR->pchData = pchPacket;
  1796. //
  1797. // verify datalength does not extend beyond packet end
  1798. //
  1799. pchPacket += pParsedRR->DataLength;
  1800. pParsedRR->pchNextRR = pchPacket;
  1801. if ( pchPacket > pchMsgEnd )
  1802. {
  1803. DNS_PRINT((
  1804. "ERROR: record data at %p (length %d) is not within packet!.\n",
  1805. pchPacket - pParsedRR->DataLength,
  1806. pParsedRR->DataLength ));
  1807. return( 0 );
  1808. }
  1809. //
  1810. // return ptr to next record in packet
  1811. //
  1812. return( pchPacket );
  1813. }
  1814. PDNS_MSG_BUF
  1815. Dns_BuildPacket(
  1816. IN PDNS_HEADER pHeader,
  1817. IN BOOL fNoHeaderCounts,
  1818. IN LPSTR pszQuestionName,
  1819. IN WORD wQuestionType,
  1820. IN PDNS_RECORD pRecords,
  1821. IN DWORD dwFlags,
  1822. IN BOOL fUpdatePacket
  1823. )
  1824. /*++
  1825. Routine Description:
  1826. Build packet.
  1827. Arguments:
  1828. pHeader -- DNS header to send
  1829. fNoHeaderCounts - do NOT include record counts in copying header
  1830. pszName -- DNS name to query
  1831. wType -- query type
  1832. pRecords -- address to receive ptr to record list returned from query
  1833. dwFlags -- query flags
  1834. aipDnsServers -- specific DNS servers to query;
  1835. OPTIONAL, if specified overrides normal list associated with machine
  1836. pDnsNetAdapters -- DNS servers to query; if NULL get current list
  1837. fUpdatePacket -- If TRUE, the packet is going to contain an update.
  1838. Therefore the section flags in the pRecords
  1839. should be interpreted for update. Otherwise this is
  1840. for a query and section flags will be interpreted for
  1841. query.
  1842. Return Value:
  1843. ERROR_SUCCESS if successful.
  1844. Error code on failure.
  1845. --*/
  1846. {
  1847. PDNS_MSG_BUF pmsg;
  1848. DWORD length;
  1849. DNS_STATUS status;
  1850. DNSDBG( WRITE, (
  1851. "Dns_BuildPacket()\n"
  1852. "\tname %s\n"
  1853. "\ttype %d\n"
  1854. "\theader %p\n"
  1855. "\t - counts %d\n"
  1856. "\trecords %p\n"
  1857. "\tflags %p\n"
  1858. "\tfUpdatePacket %p\n",
  1859. pszQuestionName,
  1860. wQuestionType,
  1861. pHeader,
  1862. fNoHeaderCounts,
  1863. pRecords,
  1864. dwFlags,
  1865. fUpdatePacket ));
  1866. //
  1867. // allocate packet
  1868. // - if just a question, standard UDP will do it
  1869. // - if contains records, then use TCP buffer
  1870. //
  1871. length = 0;
  1872. if ( pRecords )
  1873. {
  1874. length = DNS_TCP_DEFAULT_PACKET_LENGTH;
  1875. }
  1876. pmsg = Dns_AllocateMsgBuf( (WORD)length );
  1877. if ( !pmsg )
  1878. {
  1879. status = DNS_ERROR_NO_MEMORY;
  1880. goto Failed;
  1881. }
  1882. //
  1883. // write question?
  1884. //
  1885. if ( pszQuestionName )
  1886. {
  1887. if ( ! Dns_WriteQuestionToMessage(
  1888. pmsg,
  1889. pszQuestionName,
  1890. wQuestionType,
  1891. (BOOL)!!(dwFlags & DNSQUERY_UNICODE_NAME)
  1892. ) )
  1893. {
  1894. status = ERROR_INVALID_NAME;
  1895. goto Failed;
  1896. }
  1897. }
  1898. //
  1899. // build packet records
  1900. //
  1901. if ( pRecords )
  1902. {
  1903. status = Dns_AddRecordsToMessage(
  1904. pmsg,
  1905. pRecords,
  1906. fUpdatePacket );
  1907. if ( status != ERROR_SUCCESS )
  1908. {
  1909. DNS_PRINT((
  1910. "ERROR: failure writing records to message.\n" ));
  1911. goto Failed;
  1912. }
  1913. }
  1914. //
  1915. // overwrite header?
  1916. //
  1917. if ( pHeader )
  1918. {
  1919. length = sizeof(DNS_HEADER);
  1920. if ( fNoHeaderCounts )
  1921. {
  1922. length = sizeof(DWORD);
  1923. }
  1924. RtlCopyMemory(
  1925. & pmsg->MessageHead,
  1926. pHeader,
  1927. length );
  1928. }
  1929. // override recursion default, if desired
  1930. if ( dwFlags & DNS_QUERY_NO_RECURSION )
  1931. {
  1932. pmsg->MessageHead.RecursionDesired = FALSE;
  1933. }
  1934. // init XID if not set
  1935. if ( pmsg->MessageHead.Xid == 0 )
  1936. {
  1937. pmsg->MessageHead.Xid = gwTransactionId++;
  1938. }
  1939. return( pmsg );
  1940. Failed:
  1941. SetLastError( status );
  1942. FREE_HEAP( pmsg );
  1943. return( NULL );
  1944. }
  1945. WORD
  1946. Dns_GetRandomXid(
  1947. IN PVOID pSeed
  1948. )
  1949. /*++
  1950. Routine Description:
  1951. Generate "random" XID.
  1952. Arguments:
  1953. pSeed -- seed ptr; from stack or heap, provides differentiation beyond time
  1954. Return Value:
  1955. XID generated
  1956. --*/
  1957. {
  1958. SYSTEMTIME systemTime;
  1959. //
  1960. // may have multiple sessions to different processes\threads
  1961. //
  1962. // use system time hashed in with seed pointer
  1963. // ptr is first pushed down to count 64-bit boundaries, so lack of
  1964. // randomness in last 6bits is not preserved
  1965. //
  1966. GetSystemTime( &systemTime );
  1967. // hash millisecs with arbitrary stack location after knocking off any
  1968. // 64-bit boundary constraints
  1969. return (WORD)( systemTime.wMilliseconds * (PtrToUlong(pSeed) >> 6) );
  1970. }
  1971. //
  1972. // End packet.c
  1973. //