IdDNSResolver.pas 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. $Log$
  13. 4/19/2005 BTaylor
  14. Added support for SVR and NAPTR records. (Used for SIP/VOIP) (parts by Frank Shearar)
  15. Added TResultRecord.Section, .FilterBySection , .FilterByClass
  16. DNS lookups can now be generated exactly the same as NsLookup.
  17. Improved .Assign support on many objects. QueryResult object+items can now be properly cloned.
  18. TIdDNSResolver.FDNSHeader was a public field, now it's a public readonly property, TIdDNSResolver.DNSHeader
  19. fixed TMXRecord.Parse bug, .Preference will now contain correct value.
  20. fixed TTextRecord.Parse issue. DomainKeys (yahoo's anti-spam method) can now be used.
  21. Minor cleanups/spelling errors fixed.
  22. Rev 1.26 3/21/2005 10:36:20 PM VVassiliev
  23. NextDNSLabel fix
  24. TTextRecord.Parse fix
  25. ClearInternalQuery before resolving
  26. Rev 1.25 2/9/05 2:10:34 AM RLebeau
  27. Removed compiler hint
  28. Rev 1.24 2/8/05 6:17:14 PM RLebeau
  29. Updated CreateQuery() to use Fetch() and AppendString() instead of Pos(),
  30. ToBytes(), and AppendBytes()
  31. Rev 1.23 10/26/2004 9:06:30 PM JPMugaas
  32. Updated references.
  33. Rev 1.22 2004.10.25 10:18:38 PM czhower
  34. Removed unused var.
  35. Rev 1.21 25/10/2004 15:55:28 ANeillans
  36. Bug fix:
  37. http://apps.atozedsoftware.com/cgi-bin/BBGIndy/BugBeGoneISAPI.dll/?item=122
  38. Checked in for Dennies Chang
  39. Rev 1.20 2004/7/19 ¤U¤È 09:40:52 DChang
  40. 1. fix the TIdResolver.ParseAnswers, add 2 parameters for the function to
  41. check if QueryResult should be clear or not, TIdResolver.FillResult is
  42. modified at the same time.
  43. Fix AXFR procedure, fully support BIND 8 AXFR procedures.
  44. 2. Replace the original type indicator in TQueryResult.Add.
  45. It can understand AAAA type correctly.
  46. 3. Add qtIXFR type for TIdDNSResover, add 2 parameters for
  47. TIdDNSResolver.Resolver, add one parameter for TIdDNSResolver.CreateHeader.
  48. 4. Support query type CHAOS, but only for checking version.bind. (Check DNS
  49. server version.)
  50. Rev 1.19 7/12/2004 9:42:26 PM DSiders
  51. Removed TODO for Address property.
  52. Rev 1.18 7/12/2004 9:24:04 PM DSiders
  53. Added TODOs for property name inconsistencies.
  54. Rev 1.17 7/8/04 11:48:28 PM RLebeau
  55. Tweaked TQueryResult.NextDNSLabel()
  56. Rev 1.16 2004.05.20 1:39:30 PM czhower
  57. Last of the IdStream updates
  58. Rev 1.15 2004.04.08 3:57:28 PM czhower
  59. Removal of bytes from buffer.
  60. Rev 1.14 2004.03.01 9:37:04 PM czhower
  61. Fixed name conflicts for .net
  62. Rev 1.13 2/11/2004 5:47:26 AM JPMugaas
  63. Can now assign a port for the DNS host as well as IPVersion.
  64. In addition, you can now use socks with TCP zone transfers.
  65. Rev 1.12 2/11/2004 5:21:16 AM JPMugaas
  66. Vladimir Vassiliev changes for removal of byte flipping. Network conversion
  67. order conversion functions are used instead.
  68. IPv6 addresses are returned in the standard form.
  69. In WKS records, Address was changed to IPAddress to be consistant with other
  70. record types. Address can also imply a hostname.
  71. Rev 1.11 2/9/2004 11:27:36 AM JPMugaas
  72. Some functions weren't working as expected. Renamed them to describe them
  73. better.
  74. Rev 1.10 2004.02.03 5:45:58 PM czhower
  75. Name changes
  76. Rev 1.9 11/13/2003 5:46:54 PM VVassiliev
  77. DotNet
  78. AAAA record fix
  79. Add PTR for IPV6
  80. Rev 1.8 10/25/2003 06:51:54 AM JPMugaas
  81. Updated for new API changes and tried to restore some functionality.
  82. Rev 1.7 10/19/2003 11:57:32 AM DSiders
  83. Added localization comments.
  84. Rev 1.6 2003.10.12 3:50:38 PM czhower
  85. Compile todos
  86. Rev 1.5 2003/4/30 ¤U¤È 12:39:54 DChang
  87. fix the TIdResolver.ParseAnswers, add 2 parameters for the function
  88. to check if QueryResult should be clear or not, TIdResolver.FillResult
  89. is modified at the same time.
  90. fix AXFR procedure, fully support BIND 8 AXFR procedures.
  91. Rev 1.4 4/28/2003 02:30:50 PM JPMugaas
  92. reverted back to the old one as the new one checked will not compile, has
  93. problametic dependancies on Contrs and Dialogs (both not permitted).
  94. Rev 1.2 4/28/2003 07:00:10 AM JPMugaas
  95. Should now compile.
  96. Rev 1.0 11/14/2002 02:18:34 PM JPMugaas
  97. Rev 1.3 04/26/2003 02:30:10 PM DenniesChang
  98. IdDNSResolver.
  99. Started: sometime.
  100. Finished: 2003/04/26
  101. IdDNSResolver has integrate UDP and TCP tunnel to resolve then types defined in RFC 1035,
  102. and AAAA, which is defined in RFC 1884, 1886.
  103. AXFR command, which is defined in RFC 1995, is also implemented in 2003/04/26
  104. The resolver also does not support Chaos RR. Only IN RR are supported as of this time.
  105. Part of code from Ray Malone
  106. // Dennies Chang : Combine TIdDNSSyncResolver and TIdDNSCommResolver as TIdDNSResolver.
  107. // 2003/04/26.
  108. // Dennies Chang : Rename TIdDNSResolver as TIdDNSCommonResolver. 2003/04/23
  109. // Dennies Chang : Add TIdDNSSyncClient to implement AXFR command. 2003/04/15
  110. // Dennies Chang : Add atAAAA and TAAAARecord (2002 Oct.)
  111. // Dennies Chang : Add TDNSHeader for IDHeader to maintain DNS Header, but not complete yet.
  112. // SG 28/1/02: Changed the DNSStrToDomain function according to original Author of the old comp: Ray Malone
  113. SG 10/07/01 Added support for qrStar query
  114. VV 12/09/01 Added construction of reverse query (PTR)
  115. DS 12/31/01 Corrected ReponsiblePerson spelling
  116. VV 01/02/03 TQueryResult.DNSStrToDomain fix
  117. TODO : Add structure of IDHEADER IN FIGURE }
  118. unit IdDNSResolver;
  119. interface
  120. {$i IdCompilerDefines.inc}
  121. uses
  122. Classes,
  123. IdAssignedNumbers,
  124. IdBuffer,
  125. IdComponent,
  126. IdGlobal, IdExceptionCore,
  127. IdNetworkCalculator,
  128. IdGlobalProtocols,
  129. IdDNSCommon,
  130. IdTCPClient,
  131. IdTCPConnection,
  132. IdUDPClient;
  133. (*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *)
  134. (*$HPPEMIT '#if !defined(UNICODE)' *)
  135. (*$HPPEMIT '#pragma alias "@Iddnsresolver@TIdDNSResolver@SetPortA$qqrxus"="@Iddnsresolver@TIdDNSResolver@SetPort$qqrxus"' *)
  136. (*$HPPEMIT '#else' *)
  137. (*$HPPEMIT '#pragma alias "@Iddnsresolver@TIdDNSResolver@SetPortW$qqrxus"="@Iddnsresolver@TIdDNSResolver@SetPort$qqrxus"' *)
  138. (*$HPPEMIT '#endif' *)
  139. (*$HPPEMIT '#endif' *)
  140. // TODO: when compiling with bcc64x, use this pragma instead:
  141. // #pragma comment(linker, "/alternatename:<name1>=<name2>")
  142. type
  143. { TODO : Solve problem with obsolete records }
  144. TQueryRecordTypes = (
  145. qtA, qtNS, qtMD, qtMF,
  146. qtName, qtSOA, qtMB, qtMG,
  147. qtMR, qtNull, qtWKS, qtPTR,
  148. qtHINFO, qtMINFO, qtMX, qtTXT,
  149. //qtRP, qtAfsdb, qtX25, qtISDN,
  150. qtRT, qtNSAP, qtNSAP_PTR, qtSIG,
  151. //qtKEY, qtPX, qtQPOS,
  152. qtAAAA,
  153. //qtLOC, qtNXT, qtR31, qtR32,
  154. qtService,
  155. //qtR34,
  156. qtNAPTR,
  157. //qtKX,
  158. qtCERT, qtV6Addr, qtDName, qtR40,
  159. qtOptional, qtIXFR, qtAXFR, qtSTAR);
  160. {Marked by Dennies Chang at 2004/7/14.
  161. {TXFRTypes = (xtAXFR, xtIXFR);
  162. }
  163. const
  164. // Lookup table for query record values.
  165. QueryRecordCount = 30;
  166. QueryRecordValues: array [0..QueryRecordCount] of UInt16 = (
  167. TypeCode_A, TypeCode_NS, TypeCode_MD, TypeCode_MF,
  168. TypeCode_CName, TypeCode_SOA, TypeCode_MB, TypeCode_MG,
  169. TypeCode_MR, TypeCode_NULL, TypeCode_WKS, TypeCode_PTR,
  170. TypeCode_HINFO, TypeCode_MINFO, TypeCode_MX, TypeCode_TXT,
  171. //TypeCode_RP, TypeCode_AFSDB, TypeCode_X25, TypeCode_ISDN,
  172. TypeCode_RT, TypeCode_NSAP, TypeCode_NSAP_PTR, TypeCode_SIG,
  173. //TypeCode_KEY, TypeCode_PX, TypeCode_QPOS,
  174. TypeCode_AAAA,
  175. //TypeCode_LOC, TypeCode_NXT, TypeCode_R31, TypeCode_R32,
  176. TypeCode_Service,
  177. //TypeCode_R34,
  178. TypeCode_NAPTR,
  179. //TypeCode_KX,
  180. TypeCode_CERT, TypeCode_V6Addr, TypeCode_DNAME, TypeCode_R40,
  181. TypeCode_OPTIONAL, TypeCode_IXFR, TypeCode_AXFR, TypeCode_STAR);
  182. QueryRecordTypes: Array [0..QueryRecordCount] of TQueryRecordTypes = (
  183. qtA, qtNS, qtMD, qtMF,
  184. qtName, qtSOA, qtMB, qtMG,
  185. qtMR, qtNull, qtWKS, qtPTR,
  186. qtHINFO, qtMINFO, qtMX, qtTXT,
  187. //qtRP, qtAfsdb, qtX25, qtISDN,
  188. qtRT, qtNSAP, qtNSAP_PTR, qtSIG,
  189. //qtKEY, qtPX, qtQPOS,
  190. qtAAAA,
  191. //qtLOC, qtNXT, qtR31, qtR32,
  192. qtService,
  193. //qtR34,
  194. qtNAPTR,
  195. //qtKX,
  196. qtCERT, qtV6Addr, qtDName, qtR40,
  197. qtOptional, qtIXFR, qtAXFR, qtSTAR);
  198. type
  199. TQueryType = set of TQueryRecordTypes;
  200. TResultSection = (rsAnswer, rsNameServer, rsAdditional);
  201. TResultSections = set of TResultSection;
  202. TResultRecord = class(TCollectionItem) // Rename to REsourceRecord
  203. protected
  204. FRecType: TQueryRecordTypes;
  205. FRecClass: UInt16;
  206. FName: string;
  207. FTTL: UInt32;
  208. FRDataLength: Integer;
  209. FRData: TIdBytes;
  210. FSection: TResultSection;
  211. FTypeCode: UInt16;
  212. public
  213. procedure Assign(Source: TPersistent); override;
  214. // Parse the data (descendants only)
  215. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); virtual;
  216. { TODO : This needs to change (to what? why?) }
  217. {RLebeau: because it only supports a subset of available DNS types!
  218. Adding TypeCode further below so unknown types can still be recognized.}
  219. property RecType: TQueryRecordTypes read FRecType;
  220. property RecClass: UInt16 read FRecClass;
  221. property Name: string read FName;
  222. property TTL: UInt32 read FTTL;
  223. property RDataLength: Integer read FRDataLength;
  224. property RData: TIdBytes read FRData;
  225. property Section: TResultSection read FSection;
  226. property TypeCode: UInt16 read FTypeCode;
  227. end;
  228. TResultRecordClass = class of TResultRecord;
  229. TRDATARecord = class(TResultRecord)
  230. protected
  231. FIPAddress: String;
  232. public
  233. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  234. procedure Assign(Source: TPersistent); override;
  235. property IPAddress: string read FIPAddress;
  236. end;
  237. TARecord = class(TRDATARecord)
  238. end;
  239. TAAAARecord = class (TResultRecord)
  240. protected
  241. FAddress: string;
  242. public
  243. //TODO: implement AssignTo instead of Assign. (why?)
  244. procedure Assign(Source: TPersistent); override;
  245. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  246. //
  247. property Address : string read FAddress;
  248. end;
  249. TWKSRecord = Class(TResultRecord)
  250. protected
  251. FByteCount: integer;
  252. FData: TIdBytes;
  253. FIPAddress: String;
  254. FProtocol: UInt16;
  255. //
  256. function GetABit(AIndex: Integer): UInt8;
  257. public
  258. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  259. procedure Assign(Source: TPersistent); override;
  260. //
  261. property IPAddress: String read FIPAddress;
  262. property Protocol: UInt16 read FProtocol;
  263. property BitMap[index: integer]: UInt8 read GetABit;
  264. property ByteCount: integer read FByteCount;
  265. end;
  266. TMXRecord = class(TResultRecord)
  267. protected
  268. FExchangeServer: string;
  269. FPreference: UInt16;
  270. public
  271. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  272. procedure Assign(Source: TPersistent); override;
  273. property ExchangeServer: string read FExchangeServer;
  274. property Preference: UInt16 read FPreference;
  275. end;
  276. TTextRecord = class(TResultRecord)
  277. protected
  278. FText: TStrings;
  279. public
  280. constructor Create(Collection: TCollection); override;
  281. destructor Destroy; override;
  282. procedure Assign(Source: TPersistent); override;
  283. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  284. Property Text: TStrings read FText;
  285. end;
  286. TErrorRecord = class(TResultRecord)
  287. end;
  288. THINFORecord = Class(TTextRecord)
  289. protected
  290. FCPU: String;
  291. FOS: String;
  292. public
  293. procedure Assign(Source: TPersistent); override;
  294. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  295. property CPU: String read FCPU;
  296. property OS: String read FOS;
  297. end;
  298. TMINFORecord = Class(TResultRecord)
  299. protected
  300. FResponsiblePerson: String;
  301. FErrorMailbox: String;
  302. public
  303. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  304. procedure Assign(Source: TPersistent); override;
  305. property ResponsiblePersonMailbox: String read FResponsiblePerson;
  306. property ErrorMailbox: String read FErrorMailbox;
  307. end;
  308. TSOARecord = class(TResultRecord)
  309. protected
  310. FSerial: UInt32;
  311. FMinimumTTL: UInt32;
  312. FRefresh: UInt32;
  313. FRetry: UInt32;
  314. FMNAME: string;
  315. FRNAME: string;
  316. FExpire: UInt32;
  317. public
  318. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  319. procedure Assign(Source: TPersistent); override;
  320. property Primary: string read FMNAME;
  321. property ResponsiblePerson: string read FRNAME;
  322. property Serial: UInt32 read FSerial;
  323. property Refresh: UInt32 read FRefresh;
  324. property Retry: UInt32 read FRetry;
  325. property Expire: UInt32 read FExpire;
  326. property MinimumTTL: UInt32 read FMinimumTTL;
  327. end;
  328. TNAMERecord = class(TResultRecord)
  329. protected
  330. FHostName: string;
  331. public
  332. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  333. procedure Assign(Source: TPersistent); override;
  334. property HostName: string read FHostName;
  335. end;
  336. TNSRecord = class(TNAMERecord)
  337. end;
  338. TCNRecord = class(TNAMERecord)
  339. end;
  340. TDNAMERecord = class(TNAMERecord)
  341. end;
  342. TSRVRecord = class(TResultRecord)
  343. private
  344. FService: string;
  345. FProtocol: string;
  346. FPriority: integer;
  347. FWeight: integer;
  348. FPort: integer;
  349. FTarget: string;
  350. FOriginalName: string;
  351. function IsValidIdent(const aStr:string):Boolean;
  352. function CleanIdent(const aStr:string):string;
  353. public
  354. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  355. procedure Assign(Source: TPersistent); override;
  356. property OriginalName:string read FOriginalName;
  357. property Service: string read FService;
  358. property Protocol: string read FProtocol;
  359. property Priority: integer read FPriority;
  360. property Weight: integer read FWeight;
  361. property Port: integer read FPort;
  362. property Target: string read FTarget;
  363. end;
  364. TNAPTRRecord = class(TResultRecord)
  365. private
  366. FOrder: integer;
  367. FPreference: integer;
  368. FFlags: string;
  369. FService: string;
  370. FRegExp: string;
  371. FReplacement: string;
  372. public
  373. procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override;
  374. procedure Assign(Source: TPersistent); override;
  375. property Order:integer read fOrder;
  376. property Preference:integer read fPreference;
  377. property Flags:string read fFlags;
  378. property Service:string read fService;
  379. property RegExp:string read fRegExp;
  380. property Replacement:string read fReplacement;
  381. end;
  382. TQueryResult = class(TCollection)
  383. protected
  384. FDomainName: String;
  385. FQueryClass: UInt16;
  386. FQueryType: UInt16;
  387. FQueryPointerList: TStringList;
  388. procedure SetItem(Index: Integer; Value: TResultRecord);
  389. function GetItem(Index: Integer): TResultRecord;
  390. public
  391. constructor Create;
  392. destructor Destroy; override;
  393. procedure Assign(Source: TPersistent); override;
  394. function Add(Answer: TIdBytes; var APos: Integer): TResultRecord;
  395. procedure Clear; reintroduce;
  396. procedure FilterBySection(const AKeep: TResultSections=[rsAnswer]);
  397. procedure FilterByClass(const AKeep: TResultRecordClass);
  398. Property QueryClass: UInt16 read FQueryClass;
  399. Property QueryType: UInt16 read FQueryType;
  400. Property DomainName: String read FDomainName;
  401. property Items[Index: Integer]: TResultRecord read GetItem write SetItem; default;
  402. end;
  403. TPTRRecord = Class(TNAMERecord)
  404. end;
  405. //TIdTCPConnection looks odd for something that's supposed to be UDP.
  406. //However, DNS uses TCP for zone-transfers.
  407. TIdDNSResolver = class(TIdTCPConnection)
  408. protected
  409. FAllowRecursiveQueries: boolean;
  410. FInternalQuery: TIdBytes;
  411. FQuestionLength: Integer;
  412. FHost: string;
  413. FIPVersion: TIdIPVersion;
  414. FPort: TIdPort;
  415. FQueryResult: TQueryResult;
  416. FQueryType: TQueryType;
  417. FWaitingTime: integer;
  418. FPlainTextResult: TIdBytes;
  419. FDNSHeader : TDNSHeader;
  420. procedure SetInternalQuery(const Value: TIdBytes);
  421. procedure SetPlainTextResult(const Value: TIdBytes);
  422. procedure InitComponent; override;
  423. procedure SetIPVersion(const AValue: TIdIPVersion); virtual;
  424. procedure SetPort(const AValue: TIdPort); virtual;
  425. public
  426. property DNSHeader:TDNSHeader read FDNSHeader;
  427. procedure ClearInternalQuery;
  428. destructor Destroy; override;
  429. procedure ParseAnswers(DNSHeader: TDNSHeader; Answer: TIdBytes; ResetResult: Boolean = True);
  430. procedure CreateQuery(ADomain: string; SOARR : TIdRR_SOA; QueryClass:integer = Class_IN);
  431. procedure FillResult(AResult: TIdBytes; checkID : boolean = true;
  432. ResetResult : boolean = true);
  433. procedure FillResultWithOutCheckId(AResult: TIdBytes); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use FillResult() with checkID=False'{$ENDIF};{$ENDIF}
  434. procedure Resolve(ADomain: string; SOARR : TIdRR_SOA = nil; QClass: integer = Class_IN);
  435. property QueryResult: TQueryResult read FQueryResult;
  436. property InternalQuery: TIdBytes read FInternalQuery write SetInternalQuery;
  437. property PlainTextResult: TIdBytes read FPlainTextResult write SetPlainTextResult;
  438. published
  439. property QueryType : TQueryType read FQueryType write FQueryType;
  440. // TODO: rename to ReadTimeout?
  441. // Dennies's comment : it's ok, that's just a name.
  442. property WaitingTime : integer read FWaitingTime write FWaitingTime;
  443. property AllowRecursiveQueries : boolean read FAllowRecursiveQueries write FAllowRecursiveQueries;
  444. property Host : string read FHost write FHost;
  445. property Port : TIdPort read FPort write SetPort default IdPORT_DOMAIN;
  446. property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION;
  447. end;
  448. function DNSStrToDomain(const DNSStr: TIdBytes; var VPos: Integer): string;
  449. function NextDNSLabel(const DNSStr: TIdBytes; var VPos: Integer): string;
  450. implementation
  451. uses
  452. IdBaseComponent,
  453. IdResourceStringsProtocols,
  454. IdStack, SysUtils,
  455. IdException;
  456. type
  457. EIdNotEnoughData = class(EIdException);
  458. function ParseUInt8(const Buffer: TIdBytes; var VPos: Integer): UInt8;
  459. {$IFDEF USE_INLINE}inline;{$ENDIF}
  460. begin
  461. if VPos >= Length(Buffer) then begin
  462. raise EIdNotEnoughData.Create('');
  463. end;
  464. Result := Buffer[VPos];
  465. Inc(VPos);
  466. end;
  467. function ParseUInt16(const Buffer: TIdBytes; var VPos: Integer; const AConvert: Boolean = True): UInt16; overload;
  468. {$IFDEF USE_INLINE}inline;{$ENDIF}
  469. begin
  470. if (VPos+1) >= Length(Buffer) then begin
  471. raise EIdNotEnoughData.Create('');
  472. end;
  473. // TODO can/should we use BytesToUInt16() instead of TwoByteToUInt16()?
  474. Result := TwoByteToUInt16(Buffer[VPos], Buffer[VPos + 1]);
  475. Inc(VPos, 2);
  476. if AConvert then begin
  477. Result := GStack.NetworkToHost(Result);
  478. end;
  479. end;
  480. function ParseUInt16(const Byte1, Byte2: Byte; const AConvert: Boolean = True): UInt16; overload;
  481. {$IFDEF USE_INLINE}inline;{$ENDIF}
  482. begin
  483. Result := TwoByteToUInt16(Byte1, Byte2);
  484. if AConvert then begin
  485. Result := GStack.NetworkToHost(Result);
  486. end;
  487. end;
  488. function ParseUInt32(const Buffer: TIdBytes; var VPos: Integer): UInt32;
  489. {$IFDEF USE_INLINE}inline;{$ENDIF}
  490. begin
  491. if (VPos+3) >= Length(Buffer) then begin
  492. raise EIdNotEnoughData.Create('');
  493. end;
  494. // TODO can/should we use BytesToUInt32() instead of OrdFourByteToUInt32()?
  495. Result := GStack.NetworkToHost(OrdFourByteToUInt32(Buffer[VPos], Buffer[VPos + 1], Buffer[VPos + 2], Buffer[VPos + 3]));
  496. Inc(VPos, 4);
  497. end;
  498. // SG 28/1/02: Changed that function according to original Author of the old comp: Ray Malone
  499. function DNSStrToDomain(const DNSStr: TIdBytes; var VPos: Integer): string;
  500. var
  501. LabelStr : String;
  502. Len : Integer;
  503. SavedIdx : Integer;
  504. B : Byte;
  505. PackSize: Integer;
  506. begin
  507. Result := ''; {Do not Localize}
  508. PackSize := Length(DNSStr);
  509. SavedIdx := -1;
  510. while VPos < PackSize do // name field ends with nul byte
  511. begin
  512. Len := DNSStr[VPos];
  513. Inc(VPos);
  514. // RLebeau 5/4/2009: sometimes the first entry of a domain's record is
  515. // not defined, so account for that here at the top of the loop instead
  516. // of at the bottom, otherwise a Range Check error can occur when
  517. // trying to access the non-existant data...
  518. if Len = 0 then begin
  519. Break;
  520. end;
  521. while (Len and $C0) = $C0 do // {!!0.01} added loop for pointer
  522. begin // that points to a pointer. Removed >63 hack. Am I really that stupid?
  523. if SavedIdx < 0 then begin
  524. SavedIdx := Succ(VPos); // it is important to return to original index of next element when we go down more than 1 level.
  525. end;
  526. if VPos >= Length(DNSStr) then begin
  527. raise EIdNotEnoughData.Create('');
  528. end;
  529. B := Len and $3F; // strip first two bits ($C) from first byte of offset pos
  530. VPos := ParseUInt16(B, DNSStr[VPos]);
  531. if VPos >= Length(DNSStr) then begin
  532. raise EIdNotEnoughData.Create('');
  533. end;
  534. Len := DNSStr[VPos]; // if len is another $Cx we will (while) loop again
  535. Inc(VPos);
  536. end;
  537. if VPos >= PackSize then begin
  538. raise EIdNotEnoughData.Create(''); // loop screwed up. This very very unlikely now could be removed.
  539. end;
  540. LabelStr := BytesToString(DNSStr, VPos, Len);
  541. Inc(VPos, Len);
  542. if VPos >= PackSize then begin // len byte was corrupted puting us past end of packet
  543. raise EIdNotEnoughData.Create('');
  544. end;
  545. Result := Result + LabelStr + '.'; // concat and add period. {Do not Localize}
  546. end;
  547. if TextEndsWith(Result, '.') then begin // remove final period {Do not Localize}
  548. SetLength(Result, Length(Result) - 1);
  549. end;
  550. if SavedIdx >= 0 then begin
  551. VPos := SavedIdx; // restore original Idx
  552. end;
  553. end;
  554. function NextDNSLabel(const DNSStr: TIdBytes; var VPos: Integer): string;
  555. var
  556. LabelLength: Byte;
  557. begin
  558. if VPos < Length(DNSStr) then begin
  559. LabelLength := DNSStr[VPos];
  560. Inc(VPos);
  561. //VV Shouldn't be pointers in Text messages
  562. if LabelLength > 0 then begin
  563. Result := BytesToString(DNSStr, VPos, LabelLength);
  564. Inc(VPos, LabelLength);
  565. Exit;
  566. end;
  567. end;
  568. Result := ''; {Do not Localize}
  569. end;
  570. { TARecord }
  571. procedure TRDATARecord.Assign(Source: TPersistent);
  572. begin
  573. inherited Assign(Source);
  574. if Source is TRDATARecord then begin
  575. FIPAddress := TRDATARecord(Source).IPAddress;
  576. end;
  577. end;
  578. procedure TRDATARecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  579. begin
  580. inherited Parse(CompleteMessage, APos);
  581. FIPAddress := MakeUInt32IntoIPv4Address(ParseUInt32(CompleteMessage, APos));
  582. end;
  583. { TMXRecord }
  584. procedure TMXRecord.Assign(Source: TPersistent);
  585. var
  586. LSource: TMXRecord;
  587. begin
  588. inherited Assign(Source);
  589. if Source is TMXRecord then
  590. begin
  591. LSource := TMXRecord(Source);
  592. FExchangeServer := LSource.ExchangeServer;
  593. FPreference := LSource.Preference;
  594. end;
  595. end;
  596. { TCNAMERecord }
  597. procedure TNAMERecord.Assign(Source: TPersistent);
  598. begin
  599. inherited Assign(Source);
  600. if Source is TNAMERecord then begin
  601. FHostName := TNAMERecord(Source).HostName;
  602. end;
  603. end;
  604. { TQueryResult }
  605. function TQueryResult.Add(Answer: TIdBytes; var APos: Integer): TResultRecord;
  606. var
  607. RRName: String;
  608. RR_type, RR_Class: UInt16;
  609. RR_TTL: UInt32;
  610. RD_Length: UInt16;
  611. RData: TIdBytes;
  612. begin
  613. // extract the RR data
  614. RRName := DNSStrToDomain(Answer, APos);
  615. RR_Type := ParseUInt16(Answer, APos);
  616. RR_Class := ParseUInt16(Answer, APos);
  617. RR_TTL := ParseUInt32(Answer, APos);
  618. RD_Length := ParseUInt16(Answer, APos);
  619. RData := Copy(Answer, APos, RD_Length);
  620. // remove what we have read from the buffer
  621. // Read the record type
  622. // Dennies Chang had modified this part to indicate type by RR_type
  623. // because RR_type is integer, we can use TypeCode which is defined
  624. // in IdDNSCommon to select all record type.
  625. case RR_Type of
  626. TypeCode_A ://qtA:
  627. begin
  628. Result := TARecord.Create(Self);
  629. end;
  630. TypeCode_NS : //qtNS:
  631. begin
  632. Result := TNSRecord.Create(Self);
  633. end;
  634. TypeCode_MX ://qtMX:
  635. begin
  636. Result := TMXRecord.Create(Self);
  637. end;
  638. TypeCode_CName : // qtName:
  639. begin
  640. Result := TNAMERecord.Create(Self);
  641. end;
  642. TypeCode_SOA : //qtSOA:
  643. begin
  644. Result := TSOARecord.Create(Self);
  645. end;
  646. TypeCode_HINFO : //qtHINFO:
  647. begin
  648. Result := THINFORecord.Create(Self);
  649. end;
  650. TypeCode_TXT ://qtTXT:
  651. begin
  652. Result := TTextRecord.Create(Self);
  653. end;
  654. TypeCode_WKS ://qtWKS:
  655. begin
  656. Result := TWKSRecord.Create(Self);
  657. end;
  658. TypeCode_PTR :// qtPTR:
  659. begin
  660. Result := TPTRRecord.Create(Self);
  661. end;
  662. TypeCode_MINFO ://qtMINFO:
  663. begin
  664. Result := TMINFORecord.Create(Self);
  665. end;
  666. TypeCode_AAAA : //qtAAAA:
  667. begin
  668. Result := TAAAARecord.Create(Self);
  669. end;
  670. TypeCode_Service : //qtService
  671. begin
  672. Result := TSRVRecord.Create(Self);
  673. end;
  674. TypeCode_NAPTR : //qtNAPTR
  675. begin
  676. Result := TNAPTRRecord.Create(Self);
  677. end;
  678. TypeCode_DNAME : //qtDNAME
  679. begin
  680. Result := TDNAMERecord.Create(Self);
  681. end;
  682. else begin
  683. // Unsupported query type, return generic record
  684. Result := TResultRecord.Create(Self);
  685. end;
  686. end; // case
  687. try
  688. // Set the "general purpose" options
  689. //if RR_Type <= High(QueryRecordTypes) then
  690. // modified in 2004 7/15.
  691. case RR_Type of
  692. TypeCode_A: Result.FRecType := qtA;
  693. TypeCode_NS: Result.FRecType := qtNS;
  694. TypeCode_MD: Result.FRecType := qtMD;
  695. TypeCode_MF: Result.FRecType := qtMF;
  696. TypeCode_CName: Result.FRecType := qtName;
  697. TypeCode_SOA: Result.FRecType := qtSOA;
  698. TypeCode_MB: Result.FRecType := qtMB;
  699. TypeCode_MG: Result.FRecType := qtMG;
  700. TypeCode_MR: Result.FRecType := qtMR;
  701. TypeCode_NULL: Result.FRecType := qtNull;
  702. TypeCode_WKS: Result.FRecType := qtWKS;
  703. TypeCode_PTR: Result.FRecType := qtPTR;
  704. TypeCode_HINFO: Result.FRecType := qtHINFO;
  705. TypeCode_MINFO: Result.FRecType := qtMINFO;
  706. TypeCode_MX: Result.FRecType := qtMX;
  707. TypeCode_TXT: Result.FRecType := qtTXT;
  708. //TypeCode_RP: Result.FRecType := qtRP;
  709. //TypeCode_AFSDB: Result.FRecType := qtAFSDB;
  710. //TypeCode_X25: Result.FRecType := qtX25;
  711. //TypeCode_ISDN: Result.FRecType := qtISDN;
  712. TypeCode_RT: Result.FRecType := qtRT;
  713. TypeCode_NSAP: Result.FRecType := qtNSAP;
  714. TypeCode_NSAP_PTR: Result.FRecType := qtNSAP_PTR;
  715. TypeCode_SIG: Result.FRecType := qtSIG;
  716. //TypeCode_KEY: Result.FRecType := qtKEY;
  717. //TypeCode:PX: Result.FREcType := qtPX;
  718. //TypeCode_QPOS: Result.FRecType := qtQPOS;
  719. TypeCode_AAAA: Result.FRecType := qtAAAA;
  720. //TypeCode_LOC: Result.FRecType := qtLOC;
  721. //TypeCode_NXT: Result.FRecType := qtNXT;
  722. //TypeCode_R31: Result.FRecType := qtR31;
  723. //TypeCode_R32: Result.FRecType := qtR32;
  724. TypeCode_Service:Result.FRecType := qtService;
  725. //TypeCode_R34: Result.FRecType := qtR34;
  726. TypeCode_NAPTR: Result.FRecType := qtNAPTR;
  727. //TypeCode_KX: Result.FRecType := qtKX;
  728. TypeCode_CERT: Result.FRecType := qtCERT;
  729. TypeCode_V6Addr: Result.FRecType := qtV6Addr;
  730. TypeCode_DNAME: Result.FRecType := qtDName;
  731. TypeCode_R40: Result.FRecType := qtR40;
  732. TypeCode_OPTIONAL: Result.FRecType := qtOptional;
  733. TypeCode_IXFR: Result.FRecType := qtIXFR;
  734. TypeCode_AXFR: Result.FRecType := qtAXFR;
  735. TypeCode_STAR: Result.FRecType := qtSTAR;
  736. end;
  737. Result.FRecClass := RR_Class;
  738. Result.FName := RRName;
  739. Result.FTTL := RR_TTL;
  740. Result.FRData := Copy(RData, 0, Length(RData));
  741. Result.FRDataLength := RD_Length;
  742. Result.FTypeCode := RR_Type;
  743. // Parse the result
  744. // Since the DNS message can be compressed, we need to have the whole message to parse it, in case
  745. // we encounter a pointer
  746. Result.Parse(Answer, APos);
  747. except
  748. on EIdNotEnoughData do begin
  749. // let the caller handle truncated data as needed...
  750. end;
  751. end;
  752. // Set the new position
  753. Inc(APos, RD_Length);
  754. end;
  755. constructor TQueryResult.Create;
  756. begin
  757. inherited Create(TResultRecord);
  758. FQueryPointerList := TStringList.Create;
  759. end;
  760. destructor TQueryResult.Destroy;
  761. begin
  762. FreeAndNil(FQueryPointerList);
  763. inherited Destroy;
  764. end;
  765. function TQueryResult.GetItem(Index: Integer): TResultRecord;
  766. begin
  767. Result := TResultRecord(inherited GetItem(Index));
  768. end;
  769. procedure TQueryResult.SetItem(Index: Integer; Value: TResultRecord);
  770. begin
  771. inherited SetItem(Index, Value);
  772. end;
  773. { TResultRecord }
  774. procedure TResultRecord.Assign(Source: TPersistent);
  775. var
  776. LSource: TResultRecord;
  777. begin
  778. if Source is TResultRecord then
  779. begin
  780. LSource := TResultRecord(Source);
  781. FRecType := LSource.RecType;
  782. FRecClass := LSource.RecClass;
  783. FName := LSource.Name;
  784. FTTL := LSource.TTL;
  785. FRDataLength := LSource.RDataLength;
  786. FRData := Copy(LSource.RData, 0, Length(LSource.RData));
  787. FSection := LSource.Section;
  788. FTypeCode := LSource.TypeCode;
  789. end else begin
  790. inherited Assign(Source);
  791. end;
  792. end;
  793. procedure TResultRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  794. begin
  795. end;
  796. { TNAMERecord }
  797. procedure TNAMERecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  798. begin
  799. inherited Parse(CompleteMessage, APos);
  800. FHostName := DNSStrToDomain(CompleteMessage, APos);
  801. end;
  802. { TQueryResult }
  803. procedure TQueryResult.Clear;
  804. begin
  805. inherited Clear;
  806. FQueryPointerList.Clear;
  807. end;
  808. procedure TQueryResult.Assign(Source: TPersistent);
  809. //TCollection.Assign doesn't create correct Item class.
  810. var
  811. i: Integer;
  812. LRec: TResultRecord;
  813. LNew: TResultRecord;
  814. begin
  815. if Source is TQueryResult then
  816. begin
  817. BeginUpdate;
  818. try
  819. Clear;
  820. for i := 0 to TQueryResult(Source).Count-1 do
  821. begin
  822. LRec := TQueryResult(Source).Items[i];
  823. LNew := TResultRecordClass(LRec.ClassType).Create(Self);
  824. try
  825. LNew.Assign(LRec);
  826. except
  827. FreeAndNil(LNew);
  828. raise;
  829. end;
  830. end;
  831. finally
  832. EndUpdate;
  833. end;
  834. end else begin
  835. inherited Assign(Source);
  836. end;
  837. end;
  838. { TMXRecord }
  839. procedure TMXRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  840. begin
  841. inherited Parse(CompleteMessage, APos);
  842. FPreference := ParseUInt16(CompleteMessage, APos);
  843. FExchangeServer := DNSStrToDomain(CompleteMessage, APos);
  844. end;
  845. { TTextRecord }
  846. procedure TTextRecord.Assign(Source: TPersistent);
  847. begin
  848. inherited Assign(Source);
  849. if Source is TTextRecord then begin
  850. FText.Assign(TTextRecord(Source).Text);
  851. end;
  852. end;
  853. constructor TTextRecord.Create(Collection: TCollection);
  854. begin
  855. inherited Create(Collection);
  856. FText := TStringList.Create;
  857. end;
  858. destructor TTextRecord.Destroy;
  859. begin
  860. FreeAndNil(FText);
  861. inherited Destroy;
  862. end;
  863. //the support for long text values is required for DomainKeys,
  864. //which has an encoded public key
  865. procedure TTextRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  866. var
  867. LEnd: Integer;
  868. Buffer: string;
  869. begin
  870. FText.Clear;
  871. inherited Parse(CompleteMessage, APos);
  872. LEnd := APos + Length(RData);
  873. while APos < LEnd do
  874. begin
  875. Buffer := NextDNSLabel(CompleteMessage, APos);
  876. if Buffer <> '' then begin {Do not Localize}
  877. FText.Add(Buffer);
  878. end;
  879. end;
  880. if APos > Length(CompleteMessage) then begin // len byte was corrupted puting us past end of packet
  881. raise EIdNotEnoughData.Create('');
  882. end;
  883. end;
  884. { TSOARecord }
  885. procedure TSOARecord.Assign(Source: TPersistent);
  886. var
  887. LSource: TSOARecord;
  888. begin
  889. inherited Assign(Source);
  890. if Source is TSOARecord then begin
  891. LSource := TSOARecord(Source);
  892. FSerial := LSource.Serial;
  893. FMinimumTTL := LSource.MinimumTTL;
  894. FRefresh := LSource.Refresh;
  895. FRetry := LSource.Retry;
  896. FMNAME := LSource.FMNAME;
  897. FRNAME := LSource.FRNAME;
  898. FExpire := LSource.Expire;
  899. end;
  900. end;
  901. procedure TSOARecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  902. begin
  903. inherited Parse(CompleteMessage, APos);
  904. FMNAME := DNSStrToDomain(CompleteMessage, APos);
  905. FRNAME := DNSStrToDomain(CompleteMessage, APos);
  906. FSerial := ParseUInt32(CompleteMessage, APos);
  907. FRefresh := ParseUInt32(CompleteMessage, APos);
  908. FRetry := ParseUInt32(CompleteMessage, APos);
  909. FExpire := ParseUInt32(CompleteMessage, APos);
  910. FMinimumTTL := ParseUInt32(CompleteMessage, APos);
  911. end;
  912. { TWKSRecord }
  913. procedure TWKSRecord.Assign(Source: TPersistent);
  914. var
  915. LSource: TWKSRecord;
  916. begin
  917. inherited Assign(Source);
  918. if Source is TWKSRecord then begin
  919. LSource := TWKSRecord(Source);
  920. FIPAddress := LSource.IPAddress;
  921. FProtocol := LSource.Protocol;
  922. FByteCount := LSource.ByteCount;
  923. FData := Copy(LSource.FData, 0, Length(LSource.FData));
  924. end;
  925. end;
  926. function TWKSRecord.GetABit(AIndex: Integer): UInt8;
  927. begin
  928. Result := FData[AIndex];
  929. end;
  930. procedure TWKSRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  931. begin
  932. inherited Parse(CompleteMessage, APos);
  933. APos := 0;
  934. FIPAddress := MakeUInt32IntoIPv4Address(ParseUInt32(RData, APos));
  935. FProtocol := UInt16(ParseUInt8(RData, APos));
  936. FData := Copy(RData, APos, MaxInt);
  937. end;
  938. { TMINFORecord }
  939. procedure TMINFORecord.Assign(Source: TPersistent);
  940. var
  941. LSource: TMINFORecord;
  942. begin
  943. inherited Assign(Source);
  944. if Source is TMINFORecord then
  945. begin
  946. LSource := TMINFORecord(Source);
  947. FResponsiblePerson := LSource.ResponsiblePersonMailbox;
  948. FErrorMailbox := LSource.ErrorMailbox;
  949. end;
  950. end;
  951. procedure TMINFORecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  952. begin
  953. inherited Parse(CompleteMessage, APos);
  954. FResponsiblePerson := DNSStrToDomain(CompleteMessage, APos);
  955. FErrorMailbox := DNSStrToDomain(CompleteMessage, APos);
  956. end;
  957. { THINFORecord }
  958. procedure THINFORecord.Assign(Source: TPersistent);
  959. var
  960. LSource: THINFORecord;
  961. begin
  962. inherited Assign(Source);
  963. if Source is THINFORecord then
  964. begin
  965. LSource := THINFORecord(Source);
  966. FCPU := LSource.CPU;
  967. FOS := LSource.OS;
  968. end;
  969. end;
  970. procedure THINFORecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  971. begin
  972. inherited Parse(CompleteMessage, APos);
  973. FCPU := NextDNSLabel(CompleteMessage, APos);
  974. FOS := NextDNSLabel(CompleteMessage, APos);
  975. if APos > Length(CompleteMessage) then begin // len byte was corrupted puting us past end of packet
  976. raise EIdNotEnoughData.Create('');
  977. end;
  978. end;
  979. { TAAAARecord }
  980. procedure TAAAARecord.Assign(Source: TPersistent);
  981. begin
  982. inherited Assign(Source);
  983. if Source is TAAAARecord then begin
  984. FAddress := TAAAARecord(Source).Address;
  985. end;
  986. end;
  987. procedure TAAAARecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  988. var
  989. FIP6 : TIdIPv6Address;
  990. i : Integer;
  991. begin
  992. inherited Parse(CompleteMessage, APos);
  993. if Length(RData) > 0 then begin
  994. if Length(RData) < 16 then begin
  995. raise EIdNotEnoughData.Create('');
  996. end;
  997. BytesToIPv6(RData, FIP6);
  998. for i := 0 to 7 do begin
  999. FIP6[i] := GStack.NetworkToHost(FIP6[i]);
  1000. end;
  1001. FAddress := IPv6AddressToStr(FIP6);
  1002. end;
  1003. end;
  1004. { TIdDNSResolver }
  1005. procedure TIdDNSResolver.ClearInternalQuery;
  1006. begin
  1007. SetLength(FInternalQuery, 0);
  1008. FQuestionLength := 0;
  1009. end;
  1010. procedure TIdDNSResolver.CreateQuery(ADomain: string; SOARR : TIdRR_SOA;
  1011. QueryClass:integer=1);
  1012. function DoDomainName(ADNS : String): TIdBytes;
  1013. var
  1014. BufStr : String;
  1015. LLen : Byte;
  1016. begin
  1017. SetLength(Result, 0);
  1018. while Length(ADNS) > 0 do begin
  1019. BufStr := Fetch(ADNS, '.'); {Do not Localize}
  1020. LLen := Length(BufStr);
  1021. AppendByte(Result, LLen);
  1022. AppendString(Result, BufStr, LLen);
  1023. end;
  1024. end;
  1025. function DoHostAddressV6(const ADNS: String): TIdBytes;
  1026. var
  1027. IPV6Str, IPV6Ptr: string;
  1028. i: Integer;
  1029. begin
  1030. if not IsValidIPv6(ADNS) then begin
  1031. raise EIdDnsResolverError.CreateFmt(RSQueryInvalidIpV6, [aDNS]);
  1032. end;
  1033. IPV6Str := ConvertToCanonical6IP(ADNS);
  1034. IPV6Ptr := ''; {Do not Localize}
  1035. for i := Length(IPV6Str) downto 1 do begin
  1036. if IPV6Str[i] <> ':' then begin {Do not Localize}
  1037. IPV6Ptr := IPV6Ptr + IPV6Str[i] + '.'; {Do not Localize}
  1038. end;
  1039. end;
  1040. IPV6Ptr := IPV6Ptr + 'IP6.ARPA'; {Do not Localize}
  1041. Result := DoDomainName(IPV6Ptr);
  1042. end;
  1043. function DoHostAddress(const ADNS: String): TIdBytes;
  1044. var
  1045. BufStr, First, Second, Third, Fourth: String;
  1046. LLen: Byte;
  1047. begin { DoHostAddress }
  1048. if Pos(':', ADNS) > 0 then begin {Do not Localize}
  1049. Result := DoHostAddressV6(ADNS);
  1050. end else begin
  1051. SetLength(Result, 0);
  1052. BufStr := ADNS;
  1053. First := Fetch(BufStr, '.');
  1054. Second := Fetch(BufStr, '.');
  1055. Third := Fetch(BufStr, '.');
  1056. Fourth := BufStr;
  1057. LLen := Length(Fourth);
  1058. AppendByte(Result, LLen);
  1059. AppendString(Result, Fourth, LLen);
  1060. LLen := Length(Third);
  1061. AppendByte(Result, LLen);
  1062. AppendString(Result, Third, LLen);
  1063. LLen := Length(Second);
  1064. AppendByte(Result, LLen);
  1065. AppendString(Result, Second, LLen);
  1066. LLen := Length(First);
  1067. AppendByte(Result, LLen);
  1068. AppendString(Result, First, LLen);
  1069. AppendByte(Result, 7);
  1070. AppendString(Result, 'in-addr', 7); {do not localize}
  1071. AppendByte(Result, 4);
  1072. AppendString(Result, 'arpa', 4); {do not localize}
  1073. end;
  1074. end;
  1075. var
  1076. ARecType: TQueryRecordTypes;
  1077. iQ: Integer;
  1078. AQuestion, AAuthority: TIdBytes;
  1079. TempBytes: TIdBytes;
  1080. w : UInt16;
  1081. begin
  1082. SetLength(TempBytes, 2);
  1083. SetLength(AAuthority, 0);
  1084. FDNSHeader.ID := Random(65535);
  1085. FDNSHeader.ClearByteCode;
  1086. FDNSHeader.Qr := 0;
  1087. FDNSHeader.OpCode := 0;
  1088. FDNSHeader.ANCount := 0;
  1089. FDNSHeader.NSCount := 0;
  1090. FDNSHeader.ARCount := 0;
  1091. //do not reverse the bytes because this is a bit set
  1092. FDNSHeader.RD := UInt16(FAllowRecursiveQueries);
  1093. // Iterate thru questions
  1094. { TODO : Optimize for non-double loop }
  1095. if (QueryType * [qtAXFR, qtIXFR]) <> [] then
  1096. begin
  1097. iQ := 1; // if exec AXFR, there can be only one Question.
  1098. if qtIXFR in QueryType then begin
  1099. // if exec IXFR, we must include a SOA record in Authority Section (RFC 1995)
  1100. if not Assigned(SOARR) then begin
  1101. raise EIdDnsResolverError.Create(GetErrorStr(7, 3));
  1102. end;
  1103. AAuthority := SOARR.BinQueryRecord('');
  1104. FDNSHeader.AA := 1;
  1105. end;
  1106. end else
  1107. begin
  1108. iQ := 0;
  1109. for ARecType := Low(TQueryRecordTypes) to High(TQueryRecordTypes) do begin
  1110. if ARecType in QueryType then begin
  1111. Inc(iQ);
  1112. end;
  1113. end;
  1114. FDNSHeader.ARCount := 1;
  1115. end;
  1116. FDNSHeader.QDCount := iQ;
  1117. if FDNSHeader.QDCount = 0 then begin
  1118. ClearInternalQuery;
  1119. Exit;
  1120. end;
  1121. InternalQuery := FDNSHeader.GenerateBinaryHeader;
  1122. if qtAXFR in QueryType then begin
  1123. if (IndyPos('IN-ADDR', UpperCase(ADomain)) > 0) or {Do not Localize}
  1124. (IndyPos('IP6.ARPA', UpperCase(ADomain)) > 0) then {do not localize}
  1125. begin
  1126. AppendBytes(AQuestion, DoHostAddress(ADomain));
  1127. end else
  1128. begin
  1129. AppendBytes(AQuestion, DoDomainName(ADomain));
  1130. end;
  1131. AppendByte(AQuestion, 0);
  1132. //we do this in a round about manner because HostToNetwork will not always
  1133. //work the same
  1134. w := 252;
  1135. w := GStack.HostToNetwork(w);
  1136. UInt16ToTwoBytes(w, TempBytes, 0);
  1137. AppendBytes(AQuestion, TempBytes); // Type = AXFR
  1138. w := QueryClass;
  1139. w := GStack.HostToNetwork(w);
  1140. UInt16ToTwoBytes(w, TempBytes, 0);
  1141. AppendBytes(AQuestion, TempBytes);
  1142. end
  1143. else if qtIXFR in QueryType then begin
  1144. if (IndyPos('IN-ADDR', UpperCase(ADomain)) > 0) or {Do not Localize}
  1145. (IndyPos('IP6.ARPA', UpperCase(ADomain)) > 0) then {do not localize}
  1146. begin
  1147. AppendBytes(AQuestion, DoHostAddress(ADomain));
  1148. end else
  1149. begin
  1150. AppendBytes(AQuestion, DoDomainName(ADomain));
  1151. end;
  1152. AppendByte(AQuestion, 0);
  1153. //we do this in a round about manner because HostToNetwork will not always
  1154. //work the same
  1155. w := 251;
  1156. w := GStack.HostToNetwork(w);
  1157. UInt16ToTwoBytes(w, TempBytes, 0);
  1158. AppendBytes(AQuestion, TempBytes); // Type = IXFR
  1159. w := QueryClass;
  1160. w := GStack.HostToNetwork(w);
  1161. UInt16ToTwoBytes(w, TempBytes, 0);
  1162. AppendBytes(AQuestion, TempBytes);
  1163. end else
  1164. begin
  1165. for ARecType := Low(TQueryRecordTypes) to High(TQueryRecordTypes) do begin
  1166. if ARecType in QueryType then begin
  1167. // Create the question
  1168. if (ARecType = qtPTR) and
  1169. (IndyPos('IN-ADDR', UpperCase(ADomain)) = 0) and {Do not Localize}
  1170. (IndyPos('IP6.ARPA', UpperCase(ADomain)) = 0) then {do not localize}
  1171. begin
  1172. AppendBytes(AQuestion, DoHostAddress(ADomain));
  1173. end else begin
  1174. AppendBytes(AQuestion, DoDomainName(ADomain));
  1175. end;
  1176. AppendByte(AQuestion, 0);
  1177. w := QueryRecordValues[Ord(ARecType)];
  1178. w := GStack.HostToNetwork(w);
  1179. UInt16ToTwoBytes(w, TempBytes, 0);
  1180. AppendBytes(AQuestion, TempBytes);
  1181. w := QueryClass;
  1182. w := GStack.HostToNetwork(w);
  1183. UInt16ToTwoBytes(w, TempBytes, 0);
  1184. AppendBytes(AQuestion, TempBytes);
  1185. end;
  1186. end;
  1187. end;
  1188. AppendBytes(FInternalQuery, AQuestion);
  1189. if FDNSHeader.ARCount = 1 then
  1190. begin
  1191. // Create the additional OPT record to advertise our UDP receive size
  1192. SetLength(AQuestion, 0);
  1193. AppendByte(AQuestion, 0); // domain name (root, 0-length)
  1194. w := TypeCode_OPTIONAL;
  1195. w := GStack.HostToNetwork(w);
  1196. UInt16ToTwoBytes(w, TempBytes, 0);
  1197. AppendBytes(AQuestion, TempBytes); // record type (OPT)
  1198. w := 1280{8192}; // TODO: make this configurable
  1199. w := GStack.HostToNetwork(w);
  1200. UInt16ToTwoBytes(w, TempBytes, 0);
  1201. AppendBytes(AQuestion, TempBytes); // record class (OPT UDP size)
  1202. UInt16ToTwoBytes(0, TempBytes, 0);
  1203. AppendBytes(AQuestion, TempBytes); // record TTL (OPT extended RCODE and version)
  1204. UInt16ToTwoBytes(0, TempBytes, 0);
  1205. AppendBytes(AQuestion, TempBytes); // record TTL (OPT flags)
  1206. UInt16ToTwoBytes(0, TempBytes, 0);
  1207. AppendBytes(AQuestion, TempBytes); // record data size
  1208. AppendBytes(FInternalQuery, AQuestion);
  1209. end;
  1210. FQuestionLength := Length(FInternalQuery);
  1211. FDNSHeader.ParseQuery(FInternalQuery);
  1212. end;
  1213. destructor TIdDNSResolver.Destroy;
  1214. begin
  1215. FreeAndNil(FQueryResult);
  1216. FreeAndNil(FDNSHeader);
  1217. inherited Destroy;
  1218. end;
  1219. procedure TIdDNSResolver.FillResult(AResult: TIdBytes; CheckID: Boolean = True;
  1220. ResetResult: Boolean = True);
  1221. var
  1222. ReplyId: UInt16;
  1223. NAnswers: UInt16;
  1224. begin
  1225. { TODO : Check bytes received }
  1226. // Check to see if the reply is the one waited for
  1227. if Length(AResult) < 12 then begin
  1228. raise EIdDnsResolverError.Create(GetErrorStr(5, 29));
  1229. end;
  1230. {
  1231. if Length(AResult) < Self.FQuestionLength then begin
  1232. raise EIdDnsResolverError.Create(GetErrorStr(5, 30));
  1233. end;
  1234. }
  1235. if CheckID then begin
  1236. ReplyId := ParseUInt16(AResult[0], AResult[1]);
  1237. if ReplyId <> FDNSHeader.Id then begin
  1238. raise EIdDnsResolverError.Create(GetErrorStr(4, FDNSHeader.id));
  1239. end;
  1240. end;
  1241. FDNSHeader.ParseQuery(AResult);
  1242. if FDNSHeader.RCode <> 0 then begin
  1243. raise EIdDnsResolverError.Create(GetRCodeStr(FDNSHeader.RCode));
  1244. end;
  1245. NAnswers := FDNSHeader.ANCount + FDNSHeader.NSCount + FDNSHeader.ARCount;
  1246. if NAnswers > 0 then begin
  1247. // Move Pointer to Start of answers
  1248. if Length(AResult) > 12 then begin
  1249. ParseAnswers(FDNSHeader, AResult, ResetResult);
  1250. end;
  1251. end;
  1252. end;
  1253. {$I IdDeprecatedImplBugOff.inc}
  1254. procedure TIdDNSResolver.FillResultWithOutCheckId(AResult: TIdBytes);
  1255. {$I IdDeprecatedImplBugOn.inc}
  1256. var
  1257. NAnswers: UInt16;
  1258. begin
  1259. if FDNSHeader.ParseQuery(AResult) <> 0 then begin
  1260. raise EIdDnsResolverError.Create(GetErrorStr(5, 29));
  1261. end;
  1262. {
  1263. if FDNSHeader.RCode <> 0 then begin
  1264. raise EIdDnsResolverError.Create(GetRCodeStr(FDNSHeader.RCode));
  1265. end;
  1266. }
  1267. NAnswers := FDNSHeader.ANCount + FDNSHeader.NSCount + FDNSHeader.ARCount;
  1268. if NAnswers > 0 then begin
  1269. // Move Pointer to Start of answers
  1270. if Length(AResult) > 12 then begin
  1271. ParseAnswers(FDNSHeader, AResult);
  1272. end;
  1273. end;
  1274. end;
  1275. procedure TQueryResult.FilterBySection(const AKeep: TResultSections);
  1276. var
  1277. i: Integer;
  1278. begin
  1279. for i := Count-1 downto 0 do
  1280. begin
  1281. if not (Items[i].Section in AKeep) then begin
  1282. Delete(i);
  1283. end;
  1284. end;
  1285. end;
  1286. procedure TQueryResult.FilterByClass(const AKeep: TResultRecordClass);
  1287. var
  1288. i: Integer;
  1289. begin
  1290. for i := Count-1 downto 0 do
  1291. begin
  1292. if not (Items[i] is AKeep) then begin
  1293. Delete(i);
  1294. end;
  1295. end;
  1296. end;
  1297. procedure TIdDNSResolver.InitComponent;
  1298. begin
  1299. inherited InitComponent;
  1300. FIPVersion := ID_DEFAULT_IP_VERSION;
  1301. Port := IdPORT_DOMAIN;
  1302. FQueryResult := TQueryResult.Create;
  1303. FDNSHeader := TDNSHeader.Create;
  1304. FAllowRecursiveQueries := true;
  1305. Self.WaitingTime := 5000;
  1306. end;
  1307. procedure TIdDNSResolver.ParseAnswers(DNSHeader: TDNSHeader; Answer: TIdBytes;
  1308. ResetResult: Boolean = True);
  1309. var
  1310. i: integer;
  1311. APos: Integer;
  1312. QDomain: string;
  1313. QType, QClass: UInt16;
  1314. begin
  1315. if ResetResult then begin
  1316. QueryResult.Clear;
  1317. end;
  1318. try
  1319. APos := 12; //13; // Header is 12 byte long we need next byte
  1320. // if QDCount = 1, we need to process Question first.
  1321. i := 1;
  1322. while (i <= DNSHeader.QDCount) and (APos < Length(Answer)) do begin
  1323. QDomain := DNSStrToDomain(Answer, APos);
  1324. QType := ParseUInt16(Answer, APos);
  1325. QClass := ParseUInt16(Answer, APos);
  1326. if i = 0 then
  1327. begin
  1328. // first, get the question
  1329. // extract the domain name
  1330. QueryResult.FDomainName := QDomain;
  1331. // get the query type
  1332. QueryResult.FQueryType := QType;
  1333. // get the Query Class
  1334. QueryResult.FQueryClass := QClass;
  1335. end;
  1336. Inc(i);
  1337. end;
  1338. i := 1;
  1339. while (i <= DNSHeader.ANCount) and (APos < Length(Answer)) do begin
  1340. QueryResult.Add(Answer, APos).FSection := rsAnswer;
  1341. Inc(i);
  1342. end;
  1343. i := 1;
  1344. while (i <= DNSHeader.NSCount) and (APos < Length(Answer)) do begin
  1345. QueryResult.Add(Answer, APos).FSection := rsNameServer;
  1346. Inc(i);
  1347. end;
  1348. i := 1;
  1349. while (i <= DNSHeader.ARCount) and (APos < Length(Answer)) do begin
  1350. QueryResult.Add(Answer, APos).FSection := rsAdditional;
  1351. Inc(i);
  1352. end;
  1353. except
  1354. on EIdNotEnoughData do begin
  1355. if DNSHeader.TC = 0 then begin
  1356. IndyRaiseOuterException(EIdDnsResolverError.Create(GetErrorStr(2, 3)));
  1357. end;
  1358. end;
  1359. end;
  1360. end;
  1361. procedure TIdDNSResolver.Resolve(ADomain: string; SOARR : TIdRR_SOA = nil;
  1362. QClass: integer = Class_IN);
  1363. var
  1364. UDP_Tunnel : TIdUDPClient;
  1365. TCP_Tunnel : TIdTCPClient;
  1366. LRet: Integer;
  1367. LResult: TIdBytes;
  1368. BytesReceived: Integer;
  1369. begin
  1370. if ADomain <> '' then begin
  1371. ClearInternalQuery;
  1372. end;
  1373. // Resolve queries the DNS for the records contained in the
  1374. if FQuestionLength = 0 then begin
  1375. if qtIXFR in QueryType then begin
  1376. CreateQuery(ADomain, SOARR, QClass);
  1377. end else begin
  1378. CreateQuery(ADomain, nil, QClass)
  1379. end;
  1380. end;
  1381. if FQuestionLength = 0 then begin
  1382. raise EIdDnsResolverError.CreateFmt(RSQueryInvalidQueryCount, [0]);
  1383. end;
  1384. if qtAXFR in QueryType then begin
  1385. // AXFR
  1386. TCP_Tunnel := TIdTCPClient.Create;
  1387. try
  1388. TCP_Tunnel.Host := Host;
  1389. TCP_Tunnel.Port := Port;
  1390. TCP_Tunnel.IPVersion := IPVersion;
  1391. TCP_Tunnel.IOHandler := IOHandler;
  1392. try
  1393. TCP_Tunnel.Connect;
  1394. try
  1395. TCP_Tunnel.IOHandler.Write(Int16(FQuestionLength));
  1396. TCP_Tunnel.IOHandler.Write(InternalQuery);
  1397. QueryResult.Clear;
  1398. LRet := TCP_Tunnel.IOHandler.ReadInt16;
  1399. TCP_Tunnel.IOHandler.ReadBytes(LResult, LRet, False);
  1400. PlainTextResult := LResult;
  1401. if LRet > 4 then begin
  1402. FillResult(LResult, False, False);
  1403. if QueryResult.Count = 0 then begin
  1404. raise EIdDnsResolverError.Create(GetErrorStr(2,3));
  1405. end;
  1406. end else begin
  1407. // TODO: use EIdNotEnoughData instead?
  1408. raise EIdDnsResolverError.Create(RSDNSTimeout);
  1409. end;
  1410. finally
  1411. TCP_Tunnel.Disconnect;
  1412. end;
  1413. except
  1414. on EIdConnectTimeout do begin
  1415. SetLength(FPlainTextResult, 0);
  1416. IndyRaiseOuterException(EIdDNSResolverError.Create(RSDNSTimeout));
  1417. end;
  1418. on EIdConnectException do begin
  1419. SetLength(FPlainTextResult, 0);
  1420. IndyRaiseOuterException(EIdDNSResolverError.Create(RSTunnelConnectToMasterFailed));
  1421. end;
  1422. end;
  1423. finally
  1424. FreeAndNil(TCP_Tunnel);
  1425. end;
  1426. end
  1427. else if qtIXFR in QueryType then begin
  1428. // IXFR
  1429. TCP_Tunnel := TIdTCPClient.Create;
  1430. try
  1431. TCP_Tunnel.Host := Host;
  1432. TCP_Tunnel.Port := Port;
  1433. TCP_Tunnel.IPVersion := IPVersion;
  1434. TCP_Tunnel.IOHandler := IOHandler;
  1435. { Thanks RLebeau, you fix a lot of codes which I do not spend time to do - Dennies Chang. }
  1436. try
  1437. TCP_Tunnel.Connect;
  1438. try
  1439. TCP_Tunnel.IOHandler.Write(Int16(FQuestionLength));
  1440. TCP_Tunnel.IOHandler.Write(InternalQuery);
  1441. QueryResult.Clear;
  1442. LRet := TCP_Tunnel.IOHandler.ReadInt16;
  1443. SetLength(LResult, LRet);
  1444. TCP_Tunnel.IOHandler.ReadBytes(LResult, LRet);
  1445. PlainTextResult := LResult;
  1446. if LRet > 4 then begin
  1447. FillResult(LResult, False, False);
  1448. if QueryResult.Count = 0 then begin
  1449. raise EIdDnsResolverError.Create(GetErrorStr(2,3));
  1450. end;
  1451. end else begin
  1452. // TODO: use EIdNotEnoughData instead?
  1453. raise EIdDnsResolverError.Create(RSDNSTimeout);
  1454. end;
  1455. finally
  1456. TCP_Tunnel.Disconnect;
  1457. end;
  1458. except
  1459. on EIdConnectTimeout do begin
  1460. SetLength(FPlainTextResult, 0);
  1461. IndyRaiseOuterException(EIdDNSResolverError.Create(RSDNSTimeout));
  1462. end;
  1463. on EIdConnectException do begin
  1464. SetLength(FPlainTextResult, 0);
  1465. IndyRaiseOuterException(EIdDNSResolverError.Create(RSTunnelConnectToMasterFailed));
  1466. end;
  1467. end;
  1468. finally
  1469. FreeAndNil(TCP_Tunnel);
  1470. end;
  1471. end
  1472. else begin
  1473. UDP_Tunnel := TIdUDPClient.Create;
  1474. try
  1475. UDP_Tunnel.Host := Host;
  1476. UDP_Tunnel.Port := Port;
  1477. UDP_Tunnel.IPVersion := IPVersion;
  1478. UDP_Tunnel.SendBuffer(InternalQuery);
  1479. SetLength(LResult, 8192); // TODO: make this configurable
  1480. BytesReceived := UDP_Tunnel.ReceiveBuffer(LResult, WaitingTime);
  1481. finally
  1482. FreeAndNil(UDP_Tunnel);
  1483. end;
  1484. if BytesReceived > 0 then begin
  1485. SetLength(LResult, BytesReceived);
  1486. end else begin
  1487. SetLength(LResult, 0);
  1488. end;
  1489. PlainTextResult := LResult;
  1490. if BytesReceived > 4 then begin
  1491. // TODO: if the response has the Truncation flag set, retry the query
  1492. // in TCP to handle larger responses...
  1493. FillResult(LResult);
  1494. if QueryResult.Count = 0 then begin
  1495. raise EIdDnsResolverError.Create(GetErrorStr(2,3));
  1496. end;
  1497. end else begin
  1498. // TODO: differentiate between a true Timeout versus a too-small response
  1499. {if BytesReceived > 0 then begin
  1500. raise EIdNotEnoughData.Create('');
  1501. end;}
  1502. raise EIdDnsResolverError.Create(RSDNSTimeout);
  1503. end;
  1504. end;
  1505. end;
  1506. procedure TIdDNSResolver.SetInternalQuery(const Value: TIdBytes);
  1507. begin
  1508. FQuestionLength := Length(Value);
  1509. FInternalQuery := Copy(Value, 0, FQuestionLength);
  1510. Self.FDNSHeader.ParseQuery(Value);
  1511. end;
  1512. procedure TIdDNSResolver.SetIPVersion(const AValue: TIdIPVersion);
  1513. begin
  1514. FIPVersion := AValue;
  1515. end;
  1516. procedure TIdDNSResolver.SetPlainTextResult(const Value: TIdBytes);
  1517. begin
  1518. FPlainTextResult := Copy(Value, 0, Length(Value));
  1519. end;
  1520. procedure TIdDNSResolver.SetPort(const AValue: TIdPort);
  1521. begin
  1522. FPort := AValue;
  1523. end;
  1524. procedure TSRVRecord.Assign(Source: TPersistent);
  1525. var
  1526. LSource: TSRVRecord;
  1527. begin
  1528. inherited Assign(Source);
  1529. if Source is TSRVRecord then
  1530. begin
  1531. LSource := TSRVRecord(Source);
  1532. FService := LSource.Service;
  1533. FProtocol := LSource.Protocol;
  1534. FPriority := LSource.Priority;
  1535. FWeight := LSource.Weight;
  1536. FPort := LSource.Port;
  1537. FTarget := LSource.Target;
  1538. end;
  1539. end;
  1540. function TSRVRecord.CleanIdent(const aStr: string): string;
  1541. begin
  1542. Result := Copy(aStr, 2, MaxInt);
  1543. end;
  1544. function TSRVRecord.IsValidIdent(const AStr: string): Boolean;
  1545. begin
  1546. Result := (Length(AStr) > 1) and TextStartsWith(AStr, '_'); {Do not Localize}
  1547. end;
  1548. procedure TSRVRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  1549. var
  1550. LName, LService, LProtocol: string;
  1551. begin
  1552. inherited Parse(CompleteMessage, APos);
  1553. FOriginalName := FName;
  1554. //this is to split: _sip._udp.example.com
  1555. LName := FName;
  1556. LService := Fetch(LName, '.', True, False);
  1557. LProtocol := Fetch(LName,'.', True, False);
  1558. if IsValidIdent(LService) and IsValidIdent(LProtocol) and (LName <> '') then
  1559. begin
  1560. FService := CleanIdent(LService);
  1561. FProtocol := CleanIdent(LProtocol);
  1562. FName := LName;
  1563. end;
  1564. FPriority := ParseUInt16(CompleteMessage, APos);
  1565. FWeight := ParseUInt16(CompleteMessage, APos);
  1566. FPort := ParseUInt16(CompleteMessage, APos);
  1567. FTarget := DNSStrToDomain(CompleteMessage, APos);
  1568. end;
  1569. procedure TNAPTRRecord.Assign(Source: TPersistent);
  1570. var
  1571. LSource: TNAPTRRecord;
  1572. begin
  1573. inherited Assign(Source);
  1574. if Source is TNAPTRRecord then
  1575. begin
  1576. LSource := TNAPTRRecord(Source);
  1577. FOrder := LSource.Order;
  1578. FPreference := LSource.Preference;
  1579. FFlags := LSource.FFlags;
  1580. FService := LSource.Service;
  1581. FRegExp := LSource.RegExp;
  1582. FReplacement := LSource.Replacement;
  1583. end;
  1584. end;
  1585. procedure TNAPTRRecord.Parse(CompleteMessage: TIdBytes; APos: Integer);
  1586. begin
  1587. inherited Parse(CompleteMessage, APos);
  1588. FOrder := ParseUInt16(CompleteMessage, APos);
  1589. FPreference := ParseUInt16(CompleteMessage, APos);
  1590. FFlags := NextDNSLabel(CompleteMessage, APos);
  1591. FService := NextDNSLabel(CompleteMessage, APos);
  1592. FRegExp := NextDNSLabel(CompleteMessage, APos);
  1593. if APos > Length(CompleteMessage) then begin // len byte was corrupted puting us past end of packet
  1594. raise EIdNotEnoughData.Create('');
  1595. end;
  1596. FReplacement := DNSStrToDomain(CompleteMessage, APos);
  1597. end;
  1598. end.