PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/ProSnooperFx_src/indy10.0.52_source/Protocols/IdDNSResolver.pas

http://github.com/lookias/ProSnooper
Pascal | 1455 lines | 1020 code | 152 blank | 283 comment | 76 complexity | 5162f935dfbae4c9ad99418fe39386ea MD5 | raw file
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 13800: IdDNSResolver.pas
  11. {
  12. { Rev 1.23 10/26/2004 9:06:30 PM JPMugaas
  13. { Updated references.
  14. }
  15. {
  16. { Rev 1.22 2004.10.25 10:18:38 PM czhower
  17. { Removed unused var.
  18. }
  19. {
  20. { Rev 1.21 25/10/2004 15:55:28 ANeillans
  21. { Bug fix:
  22. { http://apps.atozedsoftware.com/cgi-bin/BBGIndy/BugBeGoneISAPI.dll/?item=122
  23. {
  24. { Checked in for Dennies Chang
  25. }
  26. {
  27. { Rev 1.20 2004/7/19 ¤U¤È 09:40:52 DChang
  28. { 1. fix the TIdResolver.ParseAnswers, add 2 parameters for the function to
  29. { check if QueryResult should be clear or not, TIdResolver.FillResult is
  30. { modified at the same time.
  31. {
  32. { Fix AXFR procedure, fully support BIND 8 AXFR procedures.
  33. {
  34. { 2. Replace the original type indicator in TQueryResult.Add
  35. { . It can understand AAAA type correctly.
  36. {
  37. { 3. Add qtIXFR type for TIdDNSResover, add 2 parameters for
  38. { TIdDNSResolver.Resolver, add one parameter for TIdDNSResolver.CreateHeader.
  39. {
  40. { 4. Support query type CHAOS, but only for checking version.bind. (Check DNS
  41. { server version.)
  42. }
  43. {
  44. Rev 1.19 7/12/2004 9:42:26 PM DSiders
  45. Removed TODO for Address property.
  46. }
  47. {
  48. Rev 1.18 7/12/2004 9:24:04 PM DSiders
  49. Added TODOs for property name inconsistencies.
  50. }
  51. {
  52. { Rev 1.17 7/8/04 11:48:28 PM RLebeau
  53. { Tweaked TQueryResult.NextDNSLabel()
  54. }
  55. {
  56. { Rev 1.16 2004.05.20 1:39:30 PM czhower
  57. { Last of the IdStream updates
  58. }
  59. {
  60. { Rev 1.15 2004.04.08 3:57:28 PM czhower
  61. { Removal of bytes from buffer.
  62. }
  63. {
  64. { Rev 1.14 2004.03.01 9:37:04 PM czhower
  65. { Fixed name conflicts for .net
  66. }
  67. {
  68. { Rev 1.13 2/11/2004 5:47:26 AM JPMugaas
  69. { Can now assign a port for the DNS host as well as IPVersion.
  70. {
  71. { In addition, you can now use socks with TCP zone transfers.
  72. }
  73. {
  74. { Rev 1.12 2/11/2004 5:21:16 AM JPMugaas
  75. { Vladimir Vassiliev changes for removal of byte flipping. Network conversion
  76. { order conversion functions are used instead.
  77. { IPv6 addresses are returned in the standard form.
  78. { In WKS records, Address was changed to IPAddress to be consistant with other
  79. { record types. Address can also imply a hostname.
  80. }
  81. {
  82. { Rev 1.11 2/9/2004 11:27:36 AM JPMugaas
  83. { Some functions weren't working as expected. Renamed them to describe them
  84. { better.
  85. }
  86. {
  87. { Rev 1.10 2004.02.03 5:45:58 PM czhower
  88. { Name changes
  89. }
  90. {
  91. { Rev 1.9 11/13/2003 5:46:54 PM VVassiliev
  92. { DotNet
  93. { AAAA record fix
  94. { Add PTR for IPV6
  95. }
  96. {
  97. { Rev 1.8 10/25/2003 06:51:54 AM JPMugaas
  98. { Updated for new API changes and tried to restore some functionality.
  99. }
  100. {
  101. Rev 1.7 10/19/2003 11:57:32 AM DSiders
  102. Added localization comments.
  103. }
  104. {
  105. { Rev 1.6 2003.10.12 3:50:38 PM czhower
  106. { Compile todos
  107. }
  108. {
  109. { Rev 1.5 2003/4/30 ¤U¤È 12:39:54 DChang
  110. { fix the TIdResolver.ParseAnswers, add 2 parameters for the function
  111. { to check if QueryResult should be clear or not, TIdResolver.FillResult
  112. { is modified at the same time.
  113. { fix AXFR procedure, fully support BIND 8 AXFR procedures.
  114. }
  115. {
  116. { Rev 1.4 4/28/2003 02:30:50 PM JPMugaas
  117. { reverted back to the old one as the new one checked will not compile, has
  118. { problametic dependancies on Contrs and Dialogs (both not permitted).
  119. }
  120. {
  121. { Rev 1.2 4/28/2003 07:00:10 AM JPMugaas
  122. { Should now compile.
  123. }
  124. {
  125. { Rev 1.0 11/14/2002 02:18:34 PM JPMugaas
  126. Rev 1.3 04/26/2003 02:30:10 PM DenniesChang
  127. }
  128. {
  129. IdDNSResolver.
  130. Started: sometime.
  131. Finished: 2003/04/26
  132. IdDNSResolver has integrate UDP and TCP tunnel to resolve then types defined in RFC 1035,
  133. and AAAA, which is defined in RFC 1884, 1886.
  134. AXFR command, which is defined in RFC 1995, is also implemented in 2003/04/26
  135. The resolver also does not support Chaos RR. Only IN RR are supported as of this time.
  136. Part of code from Ray Malone
  137. }
  138. // Dennies Chang : Combine TIdDNSSyncResolver and TIdDNSCommResolver as TIdDNSResolver.
  139. // 2003/04/26.
  140. // Dennies Chang : Rename TIdDNSResolver as TIdDNSCommonResolver. 2003/04/23
  141. // Dennies Chang : Add TIdDNSSyncClient to implement AXFR command. 2003/04/15
  142. // Dennies Chang : Add atAAAA and TAAAARecord (2002 Oct.)
  143. // Dennies Chang : Add TDNSHeader for IDHeader to maintain DNS Header, but not complete yet.
  144. // SG 28/1/02: Changed the DNSStrToDomain function according to original Author of the old comp: Ray Malone
  145. {SG 10/07/01 Added support for qrStar query}
  146. {VV 12/09/01 Added construction of reverse query (PTR)}
  147. {DS 12/31/01 Corrected ReponsiblePerson spelling}
  148. {VV 01/02/03 TQueryResult.DNSStrToDomain fix}
  149. { TODO : Add structure of IDHEADER IN FIGURE }
  150. unit IdDNSResolver;
  151. interface
  152. uses
  153. Classes,
  154. IdAssignedNumbers,
  155. IdBuffer,
  156. IdComponent,
  157. IdGlobal, IdExceptionCore,
  158. IdNetworkCalculator,
  159. IdGlobalProtocols,
  160. IdDNSCommon,
  161. IdTCPClient,
  162. IdTCPConnection,
  163. IdTStrings,
  164. IdUDPClient;
  165. type
  166. { TODO : Solve problem with obsolete records }
  167. TQueryRecordTypes = (
  168. qtA, qtNS, qtMD, qtMF,
  169. qtName, qtSOA, qtMB, qtMG,
  170. qtMR, qtNull, qtWKS, qtPTR,
  171. qtHINFO, qtMINFO, qtMX, qtTXT,
  172. //qtRP, qtAfsdb, qtX25, qtISDN,
  173. qtRT, qtNSAP, qtNSAP_PTR, qtSIG,
  174. //qtKEY, qtPX, qtQPOS,
  175. qtAAAA,
  176. //qtLOC, qtNXT, qtR31, qtR32,
  177. //qtService, qtR34, qtNAPTR, qtKX,
  178. qtCERT, qtV6Addr, qtDName, qtR40,
  179. qtOptional, qtIXFR, qtAXFR, qtSTAR);
  180. {Marked by Dennies Chang at 2004/7/14.
  181. {TXFRTypes = (xtAXFR, xtIXFR);
  182. }
  183. const
  184. Class_IN = 1;
  185. Class_CHAOS = 3;
  186. // Lookup table for query record values.
  187. QueryRecordValues: array [0..28] of word= (1,2,3,4,
  188. 5,6,7,8,
  189. 9,10,11,12,
  190. 13,14,15,16,
  191. //17,18,19,20,
  192. 21,22,23,24,
  193. //25,26,27,
  194. 28,
  195. //29,30,31,32,
  196. //33,34,35,36,
  197. 37,38,39,40,
  198. 41, 251, 252, 255);
  199. QueryRecordTypes: Array [0..28] of TQueryRecordTypes = (
  200. qtA, qtNS, qtMD, qtMF,
  201. qtName, qtSOA, qtMB, qtMG,
  202. qtMR, qtNull, qtWKS, qtPTR,
  203. qtHINFO, qtMINFO, qtMX, qtTXT,
  204. //qtRP, qtAfsdb, qtX25, qtISDN,
  205. qtRT, qtNSAP, qtNSAP_PTR, qtSIG,
  206. //qtKEY, qtPX, qtQPOS,
  207. qtAAAA,
  208. //qtLOC, qtNXT, qtR31, qtR32,
  209. //qtService, qtR34, qtNAPTR, qtKX,
  210. qtCERT, qtV6Addr, qtDName, qtR40,
  211. qtOptional, qtIXFR, qtAXFR, qtSTAR);
  212. type
  213. TQueryType = set of TQueryRecordTypes;
  214. TResultRecord = class(TCollectionItem) // Rename to REsourceRecord
  215. protected
  216. FRecType: TQueryRecordTypes;
  217. FRecClass: word;
  218. FName: string;
  219. FTTL: cardinal;
  220. FRData: TIdBytes;
  221. FRDataLength: Integer;
  222. public
  223. // Parse the data (descendants only)
  224. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); virtual;
  225. { TODO : This needs to change }
  226. property RecType: TQueryRecordTypes read FRecType;
  227. property RecClass: word read FRecClass;
  228. property Name: string read FName;
  229. property TTL: cardinal read FTTL;
  230. Property RDataLength: Integer read FRDataLength;
  231. property RData: TIdBytes read FRData;
  232. destructor Destroy; override;
  233. end;
  234. TRDATARecord = class(TResultRecord)
  235. protected
  236. FIPAddress: String;
  237. public
  238. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  239. constructor Create(Collection: TCollection); override;
  240. procedure Assign(Source: TPersistent); override;
  241. property IPAddress: string read FIPAddress;
  242. end;
  243. TARecord = class(TRDATARecord)
  244. end;
  245. TAAAARecord = class (TResultRecord)
  246. protected
  247. FAddress: string;
  248. public
  249. //TODO: implement AssignTo instead of Assign
  250. procedure Assign(Source: TPersistent); override;
  251. constructor Create(Collection : TCollection); override;
  252. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  253. //
  254. property Address : string read FAddress;
  255. end;
  256. TWKSRecord = Class(TResultRecord)
  257. protected
  258. FByteCount: integer;
  259. FData: TIdBytes;
  260. FIPAddress: String;
  261. FProtocol: Word;
  262. //
  263. function GetABit(AIndex: Integer): Byte;
  264. public
  265. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  266. //
  267. property IPAddress: String read FIPAddress;
  268. property Protocol: Word read FProtocol;
  269. property BitMap[index: integer]: Byte read GetABit;
  270. property ByteCount: integer read FByteCount;
  271. end;
  272. TMXRecord = class(TResultRecord)
  273. protected
  274. FExchangeServer: string;
  275. FPreference: Word;
  276. public
  277. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  278. constructor Create(Collection: TCollection); override;
  279. procedure Assign(Source: TPersistent); override;
  280. property ExchangeServer: string read FExchangeServer;
  281. property Preference: word read FPreference;
  282. end;
  283. TTextRecord = class(TResultRecord)
  284. protected
  285. FText: TIdStrings;
  286. public
  287. constructor Create(Collection: TCollection); override;
  288. destructor Destroy; override;
  289. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  290. Property Text: TIdStrings read FText;
  291. end;
  292. TErrorRecord = class(TResultRecord)
  293. end;
  294. THINFORecord = Class(TTextRecord)
  295. protected
  296. FCPU: String;
  297. FOS: String;
  298. public
  299. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  300. property CPU: String read FCPU;
  301. property OS: String read FOS;
  302. end;
  303. TMINFORecord = Class(TResultRecord)
  304. protected
  305. FResponsiblePerson: String;
  306. FErrorMailbox: String;
  307. public
  308. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  309. property ResponsiblePersonMailbox: String read FResponsiblePerson;
  310. property ErrorMailbox: String read FErrorMailbox;
  311. end;
  312. TSOARecord = class(TResultRecord)
  313. protected
  314. FSerial: cardinal;
  315. FMinimumTTL: Cardinal;
  316. FRefresh: Cardinal;
  317. FRetry: Cardinal;
  318. FMNAME: string;
  319. FRNAME: string;
  320. FExpire: Cardinal;
  321. public
  322. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  323. property Primary: string read FMNAME;
  324. property ResponsiblePerson: string read FRNAME;
  325. property Serial: cardinal read FSerial;
  326. property Refresh: Cardinal read FRefresh;
  327. property Retry: Cardinal read FRetry;
  328. property Expire: Cardinal read FExpire;
  329. property MinimumTTL: Cardinal read FMinimumTTL;
  330. end;
  331. TNAMERecord = class(TResultRecord)
  332. protected
  333. FHostName: string;
  334. public
  335. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  336. constructor Create(Collection: TCollection); override;
  337. procedure Assign(Source: TPersistent); override;
  338. property HostName: string read FHostName;
  339. end;
  340. TNSRecord = class(TNAMERecord)
  341. end;
  342. TCNRecord = class(TNAMERecord)
  343. end;
  344. TQueryResult = class(TCollection)
  345. protected
  346. FRec: TResultRecord;
  347. FDomainName: String;
  348. FQueryClass: Word;
  349. FQueryType: Word;
  350. FQueryPointerList: TIdStringList;
  351. function DNSStrToDomain(SrcStr: TIdBytes; var Idx: Integer): string;
  352. function NextDNSLabel(DNSStr: TIdBytes; Var APos: Integer): string;
  353. procedure SetItem(Index: Integer; Value: TResultRecord);
  354. function GetItem(Index: Integer): TResultRecord;
  355. function GetOwner: TPersistent; override;
  356. public
  357. constructor Create(AResultRecord: TResultRecord);
  358. destructor Destroy; override;
  359. function Add(Answer: TIdBytes; var APos: Integer): TResultRecord;
  360. procedure Clear; reintroduce;
  361. Property QueryClass: Word read FQueryClass;
  362. Property QueryType: Word read FQueryType;
  363. Property DomainName: String read FDomainName;
  364. property Items[Index: Integer]: TResultRecord read GetItem write SetItem; default;
  365. end;
  366. TPTRRecord = Class(TNAMERecord)
  367. end;
  368. //TIdTCPConnection looks odd for something that's supposed to be UDP.
  369. //However, DNS uses TCP for zone-transfers.
  370. TIdDNSResolver = class(TIdTCPConnection)
  371. protected
  372. FAllowRecursiveQueries: boolean;
  373. FInternalQuery: TIdBytes;
  374. FQuestionLength: Integer;
  375. FHost: string;
  376. FIPVersion: TIdIPVersion;
  377. FPort: TIdPort;
  378. FQueryResult: TQueryResult;
  379. FQueryType: TQueryType;
  380. FWaitingTime: integer;
  381. FPlainTextResult: TIdBytes;
  382. procedure SetAllowRecursiveQueries(const Value: boolean);
  383. procedure SetHost(const Value: string);
  384. procedure SetQuertType(const Value: TQueryType);
  385. procedure SetWaitingTime(const Value: integer);
  386. procedure SetInternalQuery(const Value: TIdBytes);
  387. procedure SetPlainTextResult(const Value: TIdBytes);
  388. procedure InitComponent; override;
  389. procedure SetIPVersion(const AValue: TIdIPVersion); virtual;
  390. function GetIPVersion: TIdIPVersion;
  391. function GetPort: TIdPort;
  392. procedure SetPort(const AValue: TIdPort); virtual;
  393. public
  394. // move here, because more types of queries need to refer to it.
  395. // 2004/7/15 Dennies Chang.
  396. FDNSHeader : TDNSHeader;
  397. procedure ClearInternalQuery;
  398. destructor Destroy; override;
  399. procedure ParseAnswers(DNSHeader: TDNSHeader; Answer: TIdBytes; AnswerNum: Cardinal;
  400. ResetResult : boolean = true);
  401. // modified by Dennies Chang in 2004/7/15.
  402. procedure CreateQuery(ADomain: string; SOARR : TIdRR_SOA; QueryClass:integer = Class_IN);
  403. procedure FillResult(AResult: TIdBytes; checkID : boolean = true;
  404. ResetResult : boolean = true);
  405. procedure FillResultWithOutCheckId(AResult: string);
  406. procedure Resolve(ADomain: string; SOARR : TIdRR_SOA = nil; QClass: integer = Class_IN);
  407. property QueryResult: TQueryResult read FQueryResult;
  408. property InternalQuery: TIdBytes read FInternalQuery write SetInternalQuery;
  409. property PlainTextResult: TIdBytes read FPlainTextResult write SetPlainTextResult;
  410. published
  411. property QueryType : TQueryType read FQueryType write SetQuertType;
  412. // TODO: rename to ReadTimeout?
  413. // Dennies's comment : it's ok, that's just a name.
  414. property WaitingTime : integer read FWaitingTime write SetWaitingTime;
  415. property AllowRecursiveQueries : boolean read FAllowRecursiveQueries write SetAllowRecursiveQueries;
  416. property Host : string read FHost write SetHost;
  417. property Port : TIdPort read FPort write FPort default IdPORT_DOMAIN;
  418. property IPVersion: TIdIPVersion read GetIPVersion write SetIPVersion;
  419. end;
  420. implementation
  421. uses
  422. IdBaseComponent,
  423. IdResourceStringsProtocols,
  424. IdStack,
  425. SysUtils;
  426. // SG 28/1/02: Changed that function according to original Author of the old comp: Ray Malone
  427. function TQueryResult.DNSStrToDomain(SrcStr: TIdBytes; var Idx: Integer): string;
  428. var
  429. LabelStr : String;
  430. Len : Integer;
  431. SavedIdx : Integer;
  432. AChar :Byte;
  433. fRPackSize: Integer;
  434. i: Integer;
  435. begin
  436. Result := ''; {Do not Localize}
  437. fRPackSize := Length(SrcStr);
  438. SavedIdx := -1;
  439. repeat
  440. Len := SrcStr[Idx];
  441. while (Len and $C0) = $C0 do // {!!0.01} added loop for pointer
  442. begin // that points to a pointer. Removed >63 hack. Am I really that stupid?
  443. if SavedIdx < 0 then
  444. begin
  445. SavedIdx := Succ(Idx); // it is important to return to original index spot
  446. end;
  447. // when we go down more than 1 level.
  448. aChar := Len and $3F; // strip first two bits ($C) from first byte of offset pos
  449. Idx := GStack.NetworkToHost(TwoByteToWord(aChar, SrcStr[Idx + 1]));// + 1; // add one to index for delphi string index //VV
  450. Len := SrcStr[Idx]; // if len is another $Cx we will (while) loop again
  451. end;
  452. Assert(Idx < fRPackSize, GetErrorStr(2, 2)); // loop screwed up. This very very unlikely now could be removed.
  453. SetLength(LabelStr, Len);
  454. if Len > 0 then
  455. begin
  456. for i := 1 to Len do
  457. begin
  458. LabelStr[i] := Char(SrcStr[Idx + i]);
  459. //Move(SrcStr[Idx + 1], LabelStr[1], Length(LabelStr));
  460. end;
  461. Inc(Idx, Length(LabelStr) + 1);
  462. end;
  463. if Pred(Idx) > fRPackSize then // len byte was corrupted puting us past end of packet
  464. raise EIdDnsResolverError.Create(GetErrorStr(2, 3));
  465. Result := Result + LabelStr + '.'; // concat and add period. {Do not Localize}
  466. until (SrcStr[Idx] = 0) or (Idx >= Length(SrcStr)); // name field ends with nul byte
  467. if Result[Length(Result)] = '.' then // remove final period {Do not Localize}
  468. begin
  469. //System.Delete(Result, Length(Result), 1);
  470. SetLength(Result, Length(Result) - 1);
  471. end;
  472. if SavedIdx >= 0 then Idx := SavedIdx; // restore original Idx +1
  473. Inc(Idx); // set to first char of next item in the resource
  474. end;
  475. function TQueryResult.NextDNSLabel(DNSStr: TIdBytes; Var APos: Integer): string;
  476. var
  477. LabelLength: Byte;
  478. function IsPointer(TestVal: Integer): boolean;
  479. begin
  480. Result := (TestVal AND $C0) <> 0;
  481. end;
  482. begin
  483. if Length(DNSStr) > APos then begin
  484. LabelLength := Integer(DNSStr[APos]);
  485. Inc(APos);
  486. if not IsPointer(LabelLength) then begin
  487. if (LabelLength > 0) then begin
  488. Result := BytesToString(DNSStr, APos, LabelLength);
  489. Inc(APos, LabelLength);
  490. Exit;
  491. end;
  492. end else begin
  493. // do not dereference pointers
  494. Inc(APos);
  495. end;
  496. end;
  497. Result := ''; {Do not Localize}
  498. end;
  499. { TARecord }
  500. procedure TRDATARecord.Assign(Source: TPersistent);
  501. begin
  502. if Source is TARecord then begin
  503. FIPAddress := TARecord(Source).IPAddress;
  504. end else begin
  505. inherited Assign(Source);
  506. end;
  507. end;
  508. constructor TRDATARecord.Create(Collection: TCollection);
  509. begin
  510. // FRecType := rtA;
  511. inherited Create(Collection);
  512. end;
  513. procedure TRDATARecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  514. begin
  515. inherited;
  516. if Length(RData) > 0 then
  517. begin
  518. FIPAddress := MakeDWordIntoIPv4Address( GStack.NetworkToHost( OrdFourByteToCardinal(RData[0], RData[1], RData[2], RData[3])));
  519. // FIPAddress := Format('%d.%d.%d.%d',[RData[0], RData[1], RData[2], RData[3]]); {Do not Localize}
  520. end;
  521. end;
  522. { TMXRecord }
  523. procedure TMXRecord.Assign(Source: TPersistent);
  524. begin
  525. if Source is TMXRecord then begin
  526. FExchangeServer := TMXRecord(Source).ExchangeServer;
  527. FPreference := TMXRecord(Source).Preference;
  528. end else begin
  529. inherited Assign(Source);
  530. end;
  531. end;
  532. constructor TMXRecord.Create(Collection: TCollection);
  533. begin
  534. // FRecType := rtMX;
  535. inherited Create(Collection);
  536. end;
  537. { TCNAMERecord }
  538. procedure TNAMERecord.Assign(Source: TPersistent);
  539. begin
  540. if Source is TNAMERecord then begin
  541. FHostName := TNAMERecord(Source).HostName;
  542. end else begin
  543. inherited Assign(Source);
  544. end;
  545. end;
  546. constructor TNAMERecord.Create(Collection: TCollection);
  547. begin
  548. // FRecType := rtCNAME;
  549. inherited Create(Collection);
  550. end;
  551. { TQueryResult }
  552. function TQueryResult.Add(Answer: TIdBytes; var APos: Integer): TResultRecord;
  553. var
  554. RRName: String;
  555. RR_type, RR_Class: word;
  556. RR_TTL: Cardinal;
  557. RD_Length: word;
  558. RData: TIdBytes;
  559. begin
  560. // extract the RR data
  561. RRName := DNSStrToDomain(Answer, APos);
  562. RR_Type := GStack.NetworkToHost( TwoByteToWord(Answer[APos], Answer[APos + 1]));
  563. RR_Class := GStack.NetworkToHost(TwoByteToWord(Answer[APos + 2], Answer[APos + 3]));
  564. RR_TTL := GStack.NetworkToHost( OrdFourByteToCardinal(Answer[APos + 4], Answer[APos + 5], Answer[APos + 6], Answer[APos + 7]));
  565. RD_Length := GStack.NetworkToHost(TwoByteToWord(Answer[APos + 8], Answer[APos + 9]));
  566. RData := Copy(Answer, APos + 10, RD_Length);
  567. // remove what we have read from the buffer
  568. // Read the record type
  569. // Dennies Chang had modified this part to indicate type by RR_type
  570. // because RR_type is integer, we can use TypeCode which is defined
  571. // in IdDNSCommon to select all record type.
  572. case RR_Type of
  573. TypeCode_A ://qtA:
  574. begin
  575. result := TARecord.Create(Self);
  576. end;
  577. TypeCode_NS : //qtNS:
  578. begin
  579. result := TNSRecord.Create(Self);
  580. end;
  581. TypeCode_MX ://qtMX:
  582. begin
  583. result := TMXRecord.Create(Self);
  584. end;
  585. TypeCode_CName : // qtName:
  586. begin
  587. result := TNAMERecord.Create(Self);
  588. end;
  589. TypeCode_SOA : //qtSOA:
  590. begin
  591. result := TSOARecord.Create(Self);
  592. end;
  593. TypeCode_HINFO : //qtHINFO:
  594. begin
  595. result := THINFORecord.Create(Self);
  596. end;
  597. TypeCode_TXT ://qtTXT:
  598. begin
  599. result := TTextRecord.Create(Self);
  600. end;
  601. TypeCode_WKS ://qtWKS:
  602. begin
  603. result := TWKSRecord.Create(Self);
  604. end;
  605. TypeCode_PTR :// qtPTR:
  606. begin
  607. result := TPTRRecord.Create(Self);
  608. end;
  609. TypeCode_MINFO ://qtMINFO:
  610. begin
  611. result := TMINFORecord.Create(Self);
  612. end;
  613. TypeCode_AAAA : //qtAAAA:
  614. begin
  615. result := TAAAARecord.Create(Self);
  616. end;
  617. else
  618. // Unsoppurted query type, return generic record
  619. result := TResultRecord.Create(self);
  620. end; // case
  621. // Set the "general purprose" options
  622. if assigned(result) then
  623. begin
  624. //if RR_Type <= High(QueryRecordTypes) then
  625. // modified in 2004 7/15.
  626. case RR_Type of
  627. TypeCode_A : result.FRecType := qtA;
  628. TypeCode_NS : result.FRecType := qtNS;
  629. TypeCode_MD : result.FRecType := qtMD;
  630. TypeCode_MF : result.FRecType := qtMF;
  631. TypeCode_CName: result.FRecType := qtName;
  632. TypeCode_SOA : result.FRecType := qtSOA;
  633. TypeCode_MB : result.FRecType := qtMB;
  634. TypeCode_MG : result.FRecType := qtMG;
  635. TypeCode_MR : result.FRecType := qtMR;
  636. TypeCode_NULL : result.FRecType := qtNull;
  637. TypeCode_WKS : result.FRecType := qtWKS;
  638. TypeCode_PTR : result.FRecType := qtPTR;
  639. TypeCode_HINFO: result.FRecType := qtHINFO;
  640. TypeCode_MINFO: result.FRecType := qtMINFO;
  641. TypeCode_MX : result.FRecType := qtMX;
  642. TypeCode_TXT : result.FRecType := qtTXT;
  643. //TypeCode_NSAP : result.FRecType := QueryRecordTypes[Ord(RR_Type) - 1];
  644. //TypeCode_NSAP_PTR : result.FRecType := QueryRecordTypes[Ord(RR_Type) - 1];
  645. TypeCode_AAAA : result.FRecType := qtAAAA;
  646. //TypeCode_LOC : result.FRecType := QueryRecordTypes[Ord(RR_Type) - 1];
  647. TypeCode_AXFR : result.FRecType := qtAXFR;
  648. //TypeCode_STAR : result.FRecType := qtSTAR;
  649. end;
  650. result.FRecClass := RR_Class;
  651. result.FName := RRName;
  652. result.FTTL := RR_TTL;
  653. Result.FRData := Copy(RData, 0{1}, RD_Length);
  654. Result.FRDataLength := RD_Length;
  655. // Parse the result
  656. // Since the DNS message can be compressed, we need to have the whole message to parse it, in case
  657. // we encounter a pointer
  658. //Result.Parse(Copy(Answer, 0{1}, APos + 9 + RD_Length), APos + 10);
  659. Result.Parse(Answer, APos + 10);
  660. end;
  661. // Set the new position
  662. inc(APos, RD_Length + 10);
  663. end;
  664. constructor TQueryResult.Create(AResultRecord: TResultRecord);
  665. begin
  666. inherited Create(TResultRecord);
  667. FRec := AResultRecord;
  668. FQueryPointerList := TIdStringList.Create;
  669. end;
  670. destructor TQueryResult.destroy;
  671. begin
  672. FQueryPointerList.Free;
  673. inherited;
  674. end;
  675. function TQueryResult.GetItem(Index: Integer): TResultRecord;
  676. begin
  677. Result := TResultRecord(inherited GetItem(Index));
  678. end;
  679. function TQueryResult.GetOwner: TPersistent;
  680. begin
  681. Result := FRec;
  682. end;
  683. procedure TQueryResult.SetItem(Index: Integer; Value: TResultRecord);
  684. begin
  685. inherited SetItem(Index, Value);
  686. end;
  687. { TResultRecord }
  688. destructor TResultRecord.Destroy;
  689. begin
  690. inherited;
  691. end;
  692. procedure TResultRecord.Parse;
  693. begin
  694. end;
  695. procedure TNAMERecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  696. begin
  697. inherited;
  698. FHostName := (Collection as TQueryResult).DNSStrToDomain(CompleteMessage, APos);
  699. end;
  700. procedure TQueryResult.Clear;
  701. begin
  702. inherited Clear;
  703. FQueryPointerList.Clear;
  704. end;
  705. procedure TMXRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  706. begin
  707. inherited;
  708. FPreference := TwoByteToWord(CompleteMessage[APos], CompleteMessage[APos + 1]);
  709. Inc(Apos, 2);
  710. FExchangeServer := (Collection as TQueryResult).DNSStrToDomain(CompleteMessage, APos);
  711. end;
  712. { TTextRecord }
  713. constructor TTextRecord.Create(Collection: TCollection);
  714. begin
  715. inherited;
  716. FText := TIdStringList.Create;
  717. end;
  718. destructor TTextRecord.Destroy;
  719. begin
  720. FText.free;
  721. inherited;
  722. end;
  723. procedure TTextRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  724. var
  725. Buffer: string;
  726. begin
  727. FText.Clear;
  728. repeat
  729. Buffer := (Collection as TQueryResult).NextDNSLabel(CompleteMessage, APos);
  730. if Buffer = '' then {Do not Localize}
  731. begin
  732. Break
  733. end
  734. else
  735. begin
  736. FText.Add(Buffer);
  737. end;
  738. until false;
  739. inherited;
  740. end;
  741. { TSOARecord }
  742. procedure TSOARecord.Parse(CompleteMessage: TIdBytes;APos: Integer);
  743. begin
  744. inherited;
  745. FMNAME := (Collection as TQueryResult).DNSStrToDomain(CompleteMessage, APos);
  746. FRNAME := (Collection as TQueryResult).DNSStrToDomain(CompleteMessage, APos);
  747. FSerial := GStack.NetworkToHost( OrdFourByteToCardinal(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3]));
  748. inc(Apos, 4);
  749. FRefresh := GStack.NetworkToHost( OrdFourByteToCardinal(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3]));
  750. inc(Apos, 4);
  751. FRetry := GStack.NetworkToHost( OrdFourByteToCardinal(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3]));
  752. inc(Apos, 4);
  753. FExpire := GStack.NetworkToHost( OrdFourByteToCardinal(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3]));
  754. inc(Apos, 4);
  755. FMinimumTTL := GStack.NetworkToHost( OrdFourByteToCardinal(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3]));
  756. end;
  757. { TWKSRecord }
  758. function TWKSRecord.GetABit(AIndex: Integer): Byte;
  759. begin
  760. Result := FData[AIndex];
  761. end;
  762. procedure TWKSRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  763. begin
  764. inherited;
  765. FIPAddress := MakeDWordIntoIPv4Address( GStack.NetworkToHost( OrdFourByteToCardinal(RData[0], RData[1], RData[2], RData[3])));
  766. //Format('%d.%d.%d.%d',[Word(RData[0]), Word(RData[1]), Word(RData[2]), Word(RData[3])]); {Do not Localize}
  767. FProtocol := Word(Rdata[4]);
  768. //TODO: This is really inefficient - just slice off the first 5 bytes instead. This code is old
  769. // and this way just because I ported it direct from an older implementation
  770. with TIdBuffer.Create do try
  771. Write(FRData);
  772. Remove(5);
  773. ExtractToBytes(FData);
  774. finally Free; end;
  775. end;
  776. { TMINFORecord }
  777. procedure TMINFORecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  778. begin
  779. inherited;
  780. FResponsiblePerson := (Collection as TQueryResult).DNSStrToDomain(CompleteMessage, APos);
  781. FErrorMailbox := (Collection as TQueryResult).DNSStrToDomain(CompleteMessage, APos);
  782. end;
  783. { THINFORecord }
  784. procedure THINFORecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  785. begin
  786. inherited;
  787. FCPU := (Collection as TQueryResult).NextDNSLabel(CompleteMessage, APos);
  788. FOS := (Collection as TQueryResult).NextDNSLabel(CompleteMessage, APos);
  789. end;
  790. { TAAAARecord }
  791. procedure TAAAARecord.Assign(Source: TPersistent);
  792. begin
  793. if Source is TAAAARecord then begin
  794. FAddress := TAAAARecord(Source).Address;
  795. end else begin
  796. inherited Assign(Source);
  797. end;
  798. end;
  799. constructor TAAAARecord.Create(Collection: TCollection);
  800. begin
  801. // FRecType := rtAAAA;
  802. inherited Create(Collection);
  803. end;
  804. procedure TAAAARecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  805. var FIP6 : TIdIPv6Address;
  806. i : Integer;
  807. begin
  808. inherited;
  809. if Length(RData) >= 15 then
  810. begin
  811. FIP6 := BytesToIPv6(RData);
  812. for i := 0 to 7 do
  813. begin
  814. FIP6[i] := GStack.NetworkToHost(FIP6[i]);
  815. end;
  816. FAddress := IPv6AddressToStr(FIP6);
  817. { := Format('%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x', {Do not Localize}
  818. { [RData[0], RData[1], RData[2], RData[3],
  819. RData[4], RData[5], RData[6], RData[7],
  820. RData[8], RData[9], RData[10], RData[11],
  821. RData[12], RData[13], RData[14], RData[15]]); }
  822. end;
  823. end;
  824. { TIdDNSResolver }
  825. procedure TIdDNSResolver.ClearInternalQuery;
  826. begin
  827. SetLength(FInternalQuery, 0);
  828. FQuestionLength := 0;
  829. end;
  830. procedure TIdDNSResolver.CreateQuery(ADomain: string; SOARR : TIdRR_SOA;
  831. QueryClass:integer=1);
  832. function DoDomainName(ADNS : String): TIdBytes;
  833. var
  834. BufStr : String;
  835. aPos : Integer;
  836. begin { DoDomainName }
  837. SetLength(Result, 0);
  838. while Length(aDns)>0 do
  839. begin
  840. aPos := Pos ( '.', aDns ); {Do not Localize}
  841. if aPos = 0 then
  842. begin
  843. aPos := Length ( aDns ) + 1;
  844. end; //if aPos = 0 then
  845. BufStr := Copy( aDns, 1, aPos -1 );
  846. Delete ( aDns, 1, aPos );
  847. AppendByte(Result, Length(BufStr));
  848. AppendBytes(Result, ToBytes(BufStr));
  849. end;
  850. end;
  851. function DoHostAddressV6(aDNS :String): TIdBytes;
  852. var
  853. IPV6str, IPV6Ptr: string;
  854. i: integer;
  855. begin
  856. if not IsValidIPv6(aDNS) then
  857. raise EIdDnsResolverError.CreateFmt(RSQueryInvalidIpV6, [aDNS]);
  858. IPV6str := ConvertToCanonical6IP(aDNS);
  859. IPV6Ptr := ''; {Do not Localize}
  860. for i := Length(IPV6str) downto 1 do
  861. begin
  862. if IPV6str[i] <> ':' then {Do not Localize}
  863. IPV6Ptr := IPV6Ptr + IPV6str[i] + '.'; {Do not Localize}
  864. end;
  865. IPV6Ptr := IPV6Ptr + 'IP6.INT'; {Do not Localize}
  866. Result := DoDomainName(IPV6Ptr);
  867. end;
  868. function DoHostAddress(aDNS :String): TIdBytes;
  869. var
  870. sBufStr, First, Second, Third, Fourth : String;
  871. begin { DoHostAddress }
  872. if pos(':', aDNS) > 0 then begin {Do not Localize}
  873. Result := DoHostAddressV6(aDNS)
  874. end else begin
  875. SetLength(Result, 0);
  876. sBufStr := aDNS;
  877. First := Fetch(sBufStr, '.');
  878. Second := Fetch(sBufStr, '.');
  879. Third := Fetch(sBufStr, '.');
  880. Fourth := sBufStr;
  881. AppendByte(Result, Length(Fourth));
  882. AppendBytes(Result, ToBytes(Fourth));
  883. AppendByte(Result, Length(Third));
  884. AppendBytes(Result, ToBytes(Third));
  885. AppendByte(Result, Length(Second));
  886. AppendBytes(Result, ToBytes(Second));
  887. AppendByte(Result, Length(First));
  888. AppendBytes(Result, ToBytes(First));
  889. AppendByte(Result, 7);
  890. AppendBytes(Result, ToBytes('in-addr')); {do not localize}
  891. AppendByte(Result, 4);
  892. AppendBytes(Result, ToBytes('arpa')); {do not localize}
  893. //AppendBytes(Result, TempStr);
  894. end;
  895. end; { DoHostAddress }
  896. var
  897. ARecType: TQueryRecordTypes;
  898. iQ: Integer;
  899. AQuestion, AAuthority: TIdBytes;
  900. TempBytes: TIdBytes;
  901. w : Word;
  902. begin
  903. SetLength(TempBytes, 2);
  904. SetLength(AAuthority, 0);
  905. FDNSHeader.ID := Random(65535);
  906. FDNSHeader.ClearByteCode;
  907. FDNSHeader.Qr := 0;
  908. FDNSHeader.OpCode := 0;
  909. FDNSHeader.ANCount := 0;
  910. FDNSHeader.NSCount :=0;
  911. FDNSHeader.ARCount :=0;
  912. //do not reverse the bytes because this is a bit set
  913. FDNSHeader.RD := Word(FAllowRecursiveQueries);
  914. iQ := 0;
  915. // Iterate thru questions
  916. { TODO : Optimize for non-double loop }
  917. if not ((qtAXFR in QueryType) and (qtIXFR in QueryType))then
  918. begin
  919. for ARecType := Low(TQueryRecordTypes) to High(TQueryRecordTypes) do
  920. begin
  921. if ARecType in QueryType then
  922. begin
  923. inc(iQ);
  924. end;
  925. end;
  926. end else
  927. begin
  928. iQ := 1; // if exec AXFR, there can be only one Question.
  929. if (qtIXFR in QueryType) then
  930. begin
  931. // if exec IXFR, we must include a SOA record in Authority Section (RFC 1995)
  932. if Assigned(SOARR) then begin
  933. AAuthority := SOARR.BinQueryRecord('');
  934. end else begin
  935. raise EIdDnsResolverError.Create(GetErrorStr(7, 3));
  936. end;
  937. FDNSHeader.AA := 1;
  938. end;
  939. end;
  940. FDNSHeader.QDCount := iQ;
  941. if FDNSHeader.QDCount = 0 then
  942. begin
  943. ClearInternalQuery;
  944. Exit;
  945. end;
  946. InternalQuery := FDNSHeader.GenerateBinaryHeader;
  947. if (qtAXFR in Self.QueryType) then begin
  948. if (IndyPos('IN-ADDR', UpperCase(ADomain)) > 0) or {Do not Localize}
  949. (IndyPos('IP6.INT', UpperCase(ADomain)) > 0) then {do not localize}
  950. begin
  951. AppendBytes(AQuestion, DoHostAddress(ADomain));
  952. AppendByte(AQuestion, 0);
  953. end else
  954. begin
  955. AppendBytes(AQuestion, DoDomainName(ADomain));
  956. AppendByte(AQuestion, 0);
  957. end;
  958. //we do this in a round about manner because HostToNetwork will not always
  959. //work the same
  960. w := 252;
  961. w := GStack.HostToNetwork(w);
  962. WordToTwoBytes(w, TempBytes, 0);
  963. AppendBytes(AQuestion, TempBytes) ; // Type = AXFR
  964. w := QueryClass;
  965. w := GStack.HostToNetwork(w);
  966. WordToTwoBytes(w, TempBytes, 0);
  967. AppendBytes(AQuestion, TempBytes);
  968. end else begin
  969. if (qtIXFR in Self.QueryType) then begin
  970. if (IndyPos('IN-ADDR', UpperCase(ADomain)) > 0) or {Do not Localize}
  971. (IndyPos('IP6.INT', UpperCase(ADomain)) > 0) then {do not localize}
  972. begin
  973. AppendBytes(AQuestion, DoHostAddress(ADomain));
  974. AppendByte(AQuestion, 0);
  975. end else
  976. begin
  977. AppendBytes(AQuestion, DoDomainName(ADomain));
  978. AppendByte(AQuestion, 0);
  979. end;
  980. //we do this in a round about manner because HostToNetwork will not always
  981. //work the same
  982. w := 251;
  983. w := GStack.HostToNetwork(w);
  984. WordToTwoBytes(w, TempBytes, 0);
  985. AppendBytes(AQuestion, TempBytes) ; // Type = IXFR
  986. w := QueryClass;
  987. w := GStack.HostToNetwork(w);
  988. WordToTwoBytes(w, TempBytes, 0);
  989. AppendBytes(AQuestion, TempBytes);
  990. end else begin
  991. for ARecType := Low(TQueryRecordTypes) to High(TQueryRecordTypes) do begin
  992. if ARecType in QueryType then begin
  993. // Create the question
  994. if (ARecType = qtPTR) and (IndyPos('IN-ADDR', UpperCase(ADomain)) = 0) and {Do not Localize}
  995. (IndyPos('IP6.INT', UpperCase(ADomain)) = 0) then begin {do not localize}
  996. AppendBytes(AQuestion, DoHostAddress(ADomain));
  997. AppendByte(AQuestion, 0);
  998. end else begin
  999. AppendBytes(AQuestion, DoDomainName(ADomain));
  1000. AppendByte(AQuestion, 0);
  1001. end;
  1002. w := QueryRecordValues[Ord(ARecType)];
  1003. w := GStack.HostToNetwork(w);
  1004. WordToTwoBytes(w, TempBytes, 0);
  1005. AppendBytes(AQuestion, TempBytes);
  1006. w := QueryClass;
  1007. w := GStack.HostToNetwork(w);
  1008. WordToTwoBytes(w, TempBytes, 0);
  1009. AppendBytes(AQuestion, TempBytes);
  1010. end;
  1011. end;
  1012. end;
  1013. end;
  1014. AppendBytes(FInternalQuery, AQuestion);
  1015. FQuestionLength := Length(FInternalQuery);
  1016. FDNSHeader.ParseQuery(FInternalQuery);
  1017. end;
  1018. destructor TIdDNSResolver.Destroy;
  1019. begin
  1020. FQueryResult.Free;
  1021. FDNSHeader.Free;
  1022. inherited Destroy;
  1023. end;
  1024. procedure TIdDNSResolver.FillResult(AResult: TIdBytes; checkID : boolean = true;
  1025. ResetResult : boolean = true);
  1026. var
  1027. ReplyId: Word;
  1028. NAnswers: Word;
  1029. begin
  1030. { TODO : Check bytes received }
  1031. // Check to see if the reply is the one waited for
  1032. if Length(AResult) < 12 then begin
  1033. raise EIdDnsResolverError.Create(GetErrorStr(5, 29));
  1034. end;
  1035. { if Length(AResult) < Self.FQuestionLength then begin
  1036. raise EIdDnsResolverError.Create(GetErrorStr(5, 30));
  1037. end; }
  1038. ReplyId := GStack.NetworkToHost(TwoByteToWord(AResult[0],AResult[1]));
  1039. if checkID then begin
  1040. if ReplyId <> FDNSHeader.Id then raise EIdDnsResolverError.Create(GetErrorStr(4, FDNSHeader.id));
  1041. end;
  1042. FDNSHeader.ParseQuery(AResult);
  1043. if FDNSHeader.RCode <> 0 then
  1044. begin
  1045. raise EIdDnsResolverError.Create(GetRCodeStr(FDNSHeader.RCode));
  1046. end;
  1047. NAnswers := FDNSHeader.ANCount + FDNSHeader.NSCount + FDNSHeader.ARCount;
  1048. if NAnswers > 0 then begin
  1049. // Move Pointer to Start of answers
  1050. if Length(AResult) > 12 then
  1051. begin
  1052. ParseAnswers(FDNSHeader, AResult, NAnswers, ResetResult);
  1053. end;
  1054. end;
  1055. end;
  1056. procedure TIdDNSResolver.FillResultWithOutCheckId(AResult: string);
  1057. var
  1058. NAnswers: Word;
  1059. //TempHeader : TDNSHeader;
  1060. InternalResult : TIdBytes;
  1061. begin
  1062. SetLength(InternalResult, 0);
  1063. InternalResult := ToBytes(AResult);
  1064. Self.FDNSHeader.ParseQuery(InternalResult);
  1065. if Length(InternalResult) < 12 then begin
  1066. Raise EIdDnsResolverError.Create(GetErrorStr(5, 29));
  1067. end;
  1068. NAnswers := Self.FDNSHeader.ANCount +
  1069. Self.FDNSHeader.NSCount + Self.FDNSHeader.ARCount;
  1070. if NAnswers > 0 then begin
  1071. // Move Pointer to Start of answers
  1072. if Length(InternalResult) > 12 then
  1073. ParseAnswers(Self.FDNSHeader, InternalResult, NAnswers);
  1074. end;
  1075. end;
  1076. function TIdDNSResolver.GetIPVersion: TIdIPVersion;
  1077. begin
  1078. result := FIPVersion;
  1079. end;
  1080. function TIdDNSResolver.GetPort: TIdPort;
  1081. begin
  1082. Result := FPort;
  1083. end;
  1084. procedure TIdDNSResolver.InitComponent;
  1085. begin
  1086. inherited;
  1087. Port := IdPORT_DOMAIN;
  1088. FQueryResult := TQueryResult.Create(nil);
  1089. FDNSHeader := TDNSHeader.Create;
  1090. FAllowRecursiveQueries := true;
  1091. Self.WaitingTime := 5000;
  1092. end;
  1093. procedure TIdDNSResolver.ParseAnswers(DNSHeader: TDNSHeader;
  1094. Answer: TIdBytes; AnswerNum: Cardinal; ResetResult : boolean = true);
  1095. var
  1096. i: integer;
  1097. APos: Integer;
  1098. begin
  1099. if ResetResult then
  1100. begin
  1101. QueryResult.Clear;
  1102. end;
  1103. APos := 12; //13; // Header is 12 byte long we need next byte
  1104. // if QDCount = 1, we need to process Question first.
  1105. if DNSHeader.QDCount = 1 then begin
  1106. // first, get the question
  1107. // extract the domain name
  1108. QueryResult.FDomainName := QueryResult.DNSStrToDomain(Answer, APos);
  1109. // get the query type
  1110. QueryResult.FQueryType := TwoByteToWord(Answer[APos], Answer[APos + 1]);
  1111. Inc(APos, 2);
  1112. // get the Query Class
  1113. QueryResult.FQueryClass := TwoByteToWord(Answer[APos], Answer[APos + 1]);
  1114. Inc(APos, 2);
  1115. end;
  1116. for i := 1 to AnswerNum do
  1117. begin
  1118. QueryResult.Add(Answer, APos);
  1119. end;
  1120. end;
  1121. procedure TIdDNSResolver.Resolve(ADomain: string; SOARR : TIdRR_SOA = nil;
  1122. QClass: integer = Class_IN);
  1123. var
  1124. UDP_Tunnel : TIdUDPClient;
  1125. TCP_Tunnel : TIdTCPClient;
  1126. LRet: Integer;
  1127. LResult: TIdBytes;
  1128. BytesReceived: Integer;
  1129. begin
  1130. // Resolve queries the DNS for the records contained in the
  1131. if FQuestionLength = 0 then begin
  1132. if not (qtIXFR in Self.QueryType) then
  1133. CreateQuery(ADomain, nil, QClass)
  1134. else CreateQuery(ADomain, SOARR, QClass);
  1135. end;
  1136. if Self.FQuestionLength = 0 then begin
  1137. raise EIdDnsResolverError.CreateFmt(RSQueryInvalidQueryCount, [0]);
  1138. end;
  1139. if not (qtAXFR in Self.QueryType) then begin
  1140. if not (qtIXFR in Self.QueryType) then begin
  1141. UDP_Tunnel := TIdUDPClient.Create(Self);
  1142. try
  1143. UDP_Tunnel.Host := Self.Host;
  1144. UDP_Tunnel.Port := Self.Port;
  1145. UDP_Tunnel.IPVersion := Self.IPVersion;
  1146. UDP_Tunnel.SendBuffer(InternalQuery);
  1147. SetLength(LResult, 8192);
  1148. BytesReceived := UDP_Tunnel.ReceiveBuffer(LResult, WaitingTime);
  1149. SetLength(LResult, BytesReceived);
  1150. if Length(LResult) > 0 then begin
  1151. PlainTextResult := LResult;
  1152. end else begin
  1153. SetLength(FPlainTextResult, 0);
  1154. end;
  1155. finally
  1156. UDP_Tunnel.Free;
  1157. end;
  1158. if Length(LResult) > 4 then begin
  1159. FillResult(LResult);
  1160. end;
  1161. {
  1162. end else begin
  1163. raise EIdDnsResolverError.Create(RSDNSTimeout);
  1164. end;
  1165. }
  1166. end else begin
  1167. // IXFR
  1168. TCP_Tunnel := TIdTCPClient.Create(Self);
  1169. try
  1170. TCP_Tunnel.Host := Self.Host;
  1171. TCP_Tunnel.Port := Self.Port;
  1172. TCP_Tunnel.IPVersion := Self.IPVersion;
  1173. TCP_Tunnel.IOHandler := IOHandler;
  1174. { Thanks RLebeau, you fix a lot of codes which I do not spend time
  1175. to do - Dennies Chang.
  1176. }
  1177. try
  1178. TCP_Tunnel.Connect;
  1179. TCP_Tunnel.IOHandler.Write(SmallInt(Self.FQuestionLength));
  1180. TCP_Tunnel.IOHandler.Write(Self.InternalQuery);
  1181. Self.QueryResult.Clear;
  1182. LRet := TCP_Tunnel.IOHandler.ReadSmallInt();
  1183. SetLength(LResult, LRet);
  1184. TCP_Tunnel.IOHandler.ReadBytes(LResult, LRet);
  1185. PlainTextResult := LResult;
  1186. if LRet > 4 then begin
  1187. FillResult(LResult, False, False);
  1188. if Self.QueryResult.Count = 0 then begin
  1189. {
  1190. for Count := 0 to Self.QueryResult.Count -1 do begin
  1191. if Self.QueryResult.Items[Count] is TSOARecord then
  1192. Inc(NSCount);
  1193. end;
  1194. end else begin
  1195. }
  1196. raise EIdDnsResolverError.Create(GetErrorStr(2,3));
  1197. end
  1198. end else raise EIdDnsResolverError.Create(RSDNSTimeout);
  1199. TCP_Tunnel.Disconnect;
  1200. except
  1201. on EIdConnectTimeout do begin
  1202. SetLength(FPlainTextResult, 0);
  1203. EIdDNSResolverError.Create(RSDNSTimeout);
  1204. end;
  1205. on EIdConnectException do begin
  1206. SetLength(FPlainTextResult, 0);
  1207. EIdDNSResolverError.Create(RSTunnelConnectToMasterFailed);
  1208. end;
  1209. end;
  1210. finally
  1211. TCP_Tunnel.Free;
  1212. end;
  1213. end;
  1214. end else begin
  1215. // AXFR
  1216. TCP_Tunnel := TIdTCPClient.Create(Self);
  1217. try
  1218. TCP_Tunnel.Host := Self.Host;
  1219. TCP_Tunnel.Port := Self.Port;
  1220. TCP_Tunnel.IPVersion := Self.IPVersion;
  1221. TCP_Tunnel.IOHandler := IOHandler;
  1222. { RLebeau - already performed above...
  1223. if Self.FQuestionLength = 0 then begin
  1224. raise EIdDnsResolverError.CreateFmt(RSQueryInvalidQueryCount, [0]);
  1225. end;
  1226. }
  1227. try
  1228. TCP_Tunnel.Connect;
  1229. TCP_Tunnel.IOHandler.Write(SmallInt(Self.FQuestionLength));
  1230. TCP_Tunnel.IOHandler.Write(Self.InternalQuery);
  1231. Self.QueryResult.Clear;
  1232. LRet := TCP_Tunnel.IOHandler.ReadSmallInt();
  1233. SetLength(LResult, LRet);
  1234. TCP_Tunnel.IOHandler.ReadBytes(LResult, LRet);
  1235. PlainTextResult := LResult;
  1236. if LRet > 4 then begin
  1237. FillResult(LResult, False, False);
  1238. {for Count := 0 to Self.QueryResult.Count -1 do begin
  1239. if Self.QueryResult.Items[Count] is TSOARecord then
  1240. Inc(NSCount);
  1241. end;
  1242. }
  1243. end else raise EIdDnsResolverError.Create(RSDNSTimeout);
  1244. TCP_Tunnel.Disconnect;
  1245. except
  1246. on EIdConnectTimeout do begin
  1247. SetLength(FPlainTextResult, 0);
  1248. EIdDNSResolverError.Create(RSDNSTimeout);
  1249. end;
  1250. on EIdConnectException do begin
  1251. SetLength(FPlainTextResult, 0);
  1252. EIdDNSResolverError.Create(RSTunnelConnectToMasterFailed);
  1253. end;
  1254. end;
  1255. finally
  1256. TCP_Tunnel.Free;
  1257. end;
  1258. end;
  1259. end;
  1260. procedure TIdDNSResolver.SetAllowRecursiveQueries(const Value: boolean);
  1261. begin
  1262. FAllowRecursiveQueries := Value;
  1263. end;
  1264. procedure TIdDNSResolver.SetHost(const Value: string);
  1265. begin
  1266. FHost := Value;
  1267. end;
  1268. procedure TIdDNSResolver.SetInternalQuery(const Value: TIdBytes);
  1269. begin
  1270. FQuestionLength := Length(Value);
  1271. SetLength(FInternalQuery, FQuestionLength);
  1272. CopyTIdByteArray(Value, 0, FInternalQuery, 0, FQuestionLength);
  1273. Self.FDNSHeader.ParseQuery(Value);
  1274. end;
  1275. procedure TIdDNSResolver.SetIPVersion(const AValue: TIdIPVersion);
  1276. begin
  1277. FIPVersion := AValue;
  1278. end;
  1279. procedure TIdDNSResolver.SetPlainTextResult(const Value: TIdBytes);
  1280. var
  1281. l: integer;
  1282. begin
  1283. l := Length(Value);
  1284. SetLength(FPlainTextResult, l);
  1285. CopyTIdByteArray(Value, 0, FPlainTextResult, 0, l);
  1286. end;
  1287. procedure TIdDNSResolver.SetPort(const AValue: TIdPort);
  1288. begin
  1289. FPort := AValue;
  1290. end;
  1291. procedure TIdDNSResolver.SetQuertType(const Value: TQueryType);
  1292. begin
  1293. FQueryType := Value;
  1294. end;
  1295. procedure TIdDNSResolver.SetWaitingTime(const Value: integer);
  1296. begin
  1297. FWaitingTime := Value;
  1298. end;
  1299. end.