netdb.pp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2003 by the Free Pascal development team
  4. Implement networking routines.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. unit netdb;
  14. {
  15. WARNING
  16. This unit hardly does any error checking. For example, stringfromlabel
  17. could easily be exploited by someone sending malicious UDP packets in
  18. order to crash your program. So if you really want to depend on this
  19. in critical programs then you'd better fix a lot of code in here.
  20. Otherwise, it appears to work pretty well.
  21. }
  22. Interface
  23. { i hsh.inc} // disappears if part of resolve.pp !!
  24. Uses Sockets;
  25. Type
  26. THostAddr = in_addr; // historical aliases for these.
  27. THostAddr6= Tin6_addr;
  28. TNetAddr = THostAddr; // but in net order.
  29. Const
  30. DNSPort = 53;
  31. MaxServers = 4;
  32. MaxResolveAddr = 10;
  33. SResolveFile = '/etc/resolv.conf';
  34. SServicesFile = '/etc/services';
  35. SHostsFile = '/etc/hosts';
  36. SNetworksFile = '/etc/networks';
  37. SProtocolFile = '/etc/protocols';
  38. MaxRecursion = 10;
  39. MaxIP4Mapped = 10;
  40. Type
  41. TDNSServerArray = Array[1..MaxServers] of THostAddr;
  42. TServiceEntry = record
  43. Name : String;
  44. Protocol : String;
  45. Port : Word;
  46. Aliases : String;
  47. end;
  48. THostEntry = record
  49. Name : String;
  50. Addr : THostAddr;
  51. Aliases : String;
  52. end;
  53. PHostEntry = ^THostEntry;
  54. THostEntryArray = Array of THostEntry;
  55. TNetworkEntry = Record
  56. Name : String;
  57. Addr : TNetAddr;
  58. Aliases : String;
  59. end;
  60. PNetworkEntry = ^TNetworkEntry;
  61. TProtocolEntry = Record
  62. Name : String;
  63. Number : integer;
  64. Aliases : String;
  65. end;
  66. PProtocolEntry = ^TProtocolEntry;
  67. PHostListEntry = ^THostListEntry;
  68. THostListEntry = Record
  69. Entry : THostEntry;
  70. Next : PHostListEntry;
  71. end;
  72. Var
  73. DNSServers : TDNSServerArray;
  74. DNSServerCount : Integer;
  75. DefaultDomainList : String;
  76. CheckResolveFileAge : Boolean;
  77. CheckHostsFileAge : Boolean;
  78. TimeOutS,TimeOutMS : Longint;
  79. Function GetDNSServers(FN : String) : Integer;
  80. Function ResolveName(HostName : String; Var Addresses : Array of THostAddr) : Integer;
  81. Function ResolveName6(HostName : String; Var Addresses : Array of THostAddr6) : Integer;
  82. Function ResolveAddress(HostAddr : THostAddr; Var Addresses : Array of String) : Integer;
  83. Function ResolveAddress6(HostAddr: THostAddr6; var Addresses: Array of string) : Integer;
  84. function IN6_IS_ADDR_V4MAPPED(HostAddr: THostAddr6): boolean;
  85. Function ResolveHostByName(HostName : String; Var H : THostEntry) : Boolean;
  86. Function ResolveHostByAddr(HostAddr : THostAddr; Var H : THostEntry) : Boolean;
  87. Function GetHostByName(HostName: String; Var H : THostEntry) : boolean;
  88. Function GetHostByAddr(Addr: THostAddr; Var H : THostEntry) : boolean;
  89. Function GetNetworkByName(NetName: String; Var N : TNetworkEntry) : boolean;
  90. Function GetNetworkByAddr(Addr: THostAddr; Var N : TNetworkEntry) : boolean;
  91. Function GetServiceByName(Const Name,Proto : String; Var E : TServiceEntry) : Boolean;
  92. Function GetServiceByPort(Port : Word;Const Proto : String; Var E : TServiceEntry) : Boolean;
  93. Function GetProtocolByName(ProtoName: String; Var H : TProtocolEntry) : boolean;
  94. Function GetProtocolByNumber(proto: Integer; Var H : TProtocolEntry) : boolean;
  95. Function ProcessHosts(FileName : String) : PHostListEntry;
  96. Function FreeHostsList(var List : PHostListEntry) : Integer;
  97. Procedure HostsListToArray(var List : PHostListEntry; Var Hosts : THostEntryArray; FreeList : Boolean);
  98. Implementation
  99. uses
  100. BaseUnix,
  101. sysutils;
  102. const
  103. { from http://www.iana.org/assignments/dns-parameters }
  104. DNSQRY_A = 1; // name to IP address
  105. DNSQRY_AAAA = 28; // name to IP6 address
  106. DNSQRY_A6 = 38; // name to IP6 (new)
  107. DNSQRY_PTR = 12; // IP address to name
  108. DNSQRY_MX = 15; // name to MX
  109. DNSQRY_TXT = 16; // name to TXT
  110. DNSQRY_CNAME = 5;
  111. // Flags 1
  112. QF_QR = $80;
  113. QF_OPCODE = $78;
  114. QF_AA = $04;
  115. QF_TC = $02; // Truncated.
  116. QF_RD = $01;
  117. // Flags 2
  118. QF_RA = $80;
  119. QF_Z = $70;
  120. QF_RCODE = $0F;
  121. Type
  122. TPayLoad = Array[0..511] of char;
  123. TQueryData = packed Record
  124. id : Array[0..1] of Byte;
  125. flags1 : Byte;
  126. flags2 : Byte;
  127. qdcount : word;
  128. ancount : word;
  129. nscount : word;
  130. arcount : word;
  131. Payload : TPayLoad;
  132. end;
  133. TRRData = Packed record // RR record
  134. Atype : Word; // Answer type
  135. AClass : Word;
  136. TTL : Cardinal;
  137. RDLength : Word;
  138. end;
  139. { ---------------------------------------------------------------------
  140. Some Parsing routines
  141. ---------------------------------------------------------------------}
  142. Const
  143. Whitespace = [' ',#9];
  144. Function NextWord(Var Line : String) : String;
  145. Var
  146. I,J : Integer;
  147. begin
  148. I:=1;
  149. While (I<=Length(Line)) and (Line[i] in Whitespace) do
  150. inc(I);
  151. J:=I;
  152. While (J<=Length(Line)) and Not (Line[J] in WhiteSpace) do
  153. inc(j);
  154. Result:=Copy(Line,I,J-I);
  155. Delete(Line,1,J);
  156. end;
  157. Function StripComment(var L : String) : Boolean;
  158. Var
  159. i : Integer;
  160. begin
  161. I:=Pos('#',L);
  162. If (I<>0) then
  163. L:=Copy(L,1,I-1)
  164. else
  165. begin
  166. I:=Pos(';',L);
  167. If (I<>0) then
  168. L:=Copy(L,1,I-1)
  169. end;
  170. Result:=Length(L)>0;
  171. end;
  172. Function MatchNameOrAlias(Const Entry,Name: String; Aliases : String) : Boolean;
  173. Var
  174. P : Integer;
  175. A : String;
  176. begin
  177. Result:=CompareText(Entry,Name)=0;
  178. If Not Result then
  179. While (Not Result) and (Length(Aliases)>0) do
  180. begin
  181. P:=Pos(',',Aliases);
  182. If (P=0) then
  183. P:=Length(Aliases)+1;
  184. A:=Copy(Aliases,1,P-1);
  185. Delete(Aliases,1,P);
  186. Result:=CompareText(A,Entry)=0;
  187. end;
  188. end;
  189. { ---------------------------------------------------------------------
  190. hosts processing
  191. ---------------------------------------------------------------------}
  192. Function GetAddr(Var L : String; Var Addr : THostAddr) : Boolean;
  193. Var
  194. S : String;
  195. i,p,a : Integer;
  196. begin
  197. Result:=True;
  198. S:=NextWord(L);
  199. Addr:=StrToNetAddr(S);
  200. // Writeln(s,'->',Addr.s_bytes[1],'.',Addr.s_bytes[2],'.',Addr.s_bytes[3],'.',Addr.s_bytes[4]);
  201. Result:=Addr.s_bytes[1]<>0;
  202. end;
  203. Function FillHostEntry (Var Entry : THostEntry; L: String) : boolean;
  204. Var
  205. H : String;
  206. begin
  207. Repeat
  208. H:=NextWord(L);
  209. If (H<>'') then
  210. if (Entry.Name='') then
  211. Entry.Name:=H
  212. else
  213. begin
  214. If (Entry.Aliases<>'') then
  215. Entry.Aliases:=Entry.Aliases+',';
  216. Entry.Aliases:=Entry.Aliases+H;
  217. end;
  218. until (H='');
  219. end;
  220. Function ProcessHosts(FileName : String) : PHostListEntry;
  221. Var
  222. F : Text;
  223. L : String;
  224. A : THostAddr;
  225. T : PHostListEntry;
  226. begin
  227. Result:=Nil;
  228. Assign(F,FileName);
  229. {$I-}
  230. Reset(F);
  231. {$I+};
  232. If (IOResult<>0) then
  233. Exit;
  234. Try
  235. While Not EOF(F) do
  236. begin
  237. Readln(F,L);
  238. If StripComment(L) then
  239. begin
  240. If GetAddr(L,A) then
  241. begin
  242. T:=New(PHostListEntry);
  243. T^.Entry.Addr:=A;
  244. FillHostEntry(T^.Entry,L);
  245. T^.Next:=Result;
  246. Result:=T;
  247. end;
  248. end;
  249. end;
  250. Finally
  251. Close(F);
  252. end;
  253. end;
  254. { Internal lookup, used in GetHostByName and friends. }
  255. Var
  256. HostsList : PHostListEntry = Nil;
  257. HostsFileAge : Longint;
  258. HostsFileName : String;
  259. Function FreeHostsList(var List : PHostListEntry) : Integer;
  260. Var
  261. P : PHostListEntry;
  262. begin
  263. Result:=0;
  264. While (List<>Nil) do
  265. begin
  266. Inc(Result);
  267. P:=List^.Next;
  268. Dispose(List);
  269. List:=P;
  270. end;
  271. end;
  272. Procedure HostsListToArray(var List : PHostListEntry; Var Hosts : THostEntryArray; FreeList : Boolean);
  273. Var
  274. P : PHostListEntry;
  275. Len : Integer;
  276. begin
  277. Len:=0;
  278. P:=List;
  279. While P<> Nil do
  280. begin
  281. Inc(Len);
  282. P:=P^.Next;
  283. end;
  284. SetLength(Hosts,Len);
  285. If (Len>0) then
  286. begin
  287. Len:=0;
  288. P:=List;
  289. While (P<>Nil) do
  290. begin
  291. Hosts[Len]:=P^.Entry;
  292. P:=P^.Next;
  293. Inc(Len);
  294. end;
  295. end;
  296. If FreeList then
  297. FreeHostsList(List);
  298. end;
  299. Procedure CheckHostsFile;
  300. Var
  301. F : Integer;
  302. begin
  303. If CheckHostsFileAge then
  304. begin
  305. F:=FileAge(SHostsFile);
  306. If HostsFileAge<F then
  307. begin
  308. // Rescan.
  309. FreeHostsList(HostsList);
  310. HostsList:=ProcessHosts(SHostsFile);
  311. HostsFileAge:=F;
  312. end;
  313. end;
  314. end;
  315. Function FindHostEntryInHostsFile(N: String; Addr: THostAddr; Var H : THostEntry) : boolean;
  316. Var
  317. F : Text;
  318. HE : THostEntry;
  319. P : PHostListEntry;
  320. begin
  321. Result:=False;
  322. CheckHostsFile;
  323. P:=HostsList;
  324. While (Not Result) and (P<>Nil) do
  325. begin
  326. HE:=P^.Entry;
  327. If (N<>'') then
  328. Result:=MatchNameOrAlias(N,HE.Name,HE.Aliases)
  329. else
  330. Result:=Cardinal(hosttonet(Addr))=Cardinal(HE.Addr);
  331. P:=P^.Next;
  332. end;
  333. If Result then
  334. begin
  335. H.Name:=HE.Name;
  336. H.Addr:=nettohost(HE.Addr);
  337. H.Aliases:=HE.Aliases;
  338. end;
  339. end;
  340. { ---------------------------------------------------------------------
  341. Resolve.conf handling
  342. ---------------------------------------------------------------------}
  343. Var
  344. ResolveFileAge : Longint;
  345. ResolveFileName : String;
  346. Function GetDNSServers(Fn : String) : Integer;
  347. Var
  348. R : Text;
  349. L : String;
  350. I : Integer;
  351. H : THostAddr;
  352. E : THostEntry;
  353. Function CheckDirective(Dir : String) : Boolean;
  354. Var
  355. P : Integer;
  356. begin
  357. P:=Pos(Dir,L);
  358. Result:=(P<>0);
  359. If Result then
  360. begin
  361. Delete(L,1,P+Length(Dir));
  362. L:=Trim(L);
  363. end;
  364. end;
  365. begin
  366. Result:=0;
  367. ResolveFileName:=Fn;
  368. ResolveFileAge:=FileAge(FN);
  369. {$i-}
  370. Assign(R,FN);
  371. Reset(R);
  372. {$i+}
  373. If (IOResult<>0) then
  374. exit;
  375. Try
  376. While not EOF(R) do
  377. begin
  378. Readln(R,L);
  379. if StripComment(L) then
  380. If CheckDirective('nameserver') then
  381. begin
  382. H:=HostToNet(StrToHostAddr(L));
  383. If (H.s_bytes[1]<>0) then
  384. begin
  385. Inc(Result);
  386. DNSServers[Result]:=H;
  387. end
  388. else if FindHostEntryInHostsFile(L,H,E) then
  389. begin
  390. Inc(Result);
  391. DNSServers[Result]:=E.Addr;
  392. end;
  393. end
  394. else if CheckDirective('domain') then
  395. DefaultDomainList:=L
  396. else if CheckDirective('search') then
  397. DefaultDomainList:=L;
  398. end;
  399. Finally
  400. Close(R);
  401. end;
  402. DNSServerCount:=Result;
  403. end;
  404. Procedure CheckResolveFile;
  405. Var
  406. F : Integer;
  407. begin
  408. If CheckResolveFileAge then
  409. begin
  410. F:=FileAge(ResolveFileName);
  411. If ResolveFileAge<F then
  412. GetDnsServers(ResolveFileName);
  413. end;
  414. end;
  415. { ---------------------------------------------------------------------
  416. Payload handling functions.
  417. ---------------------------------------------------------------------}
  418. Procedure DumpPayLoad(Q : TQueryData; L : Integer);
  419. Var
  420. i : Integer;
  421. begin
  422. Writeln('Payload : ',l);
  423. For I:=0 to L-1 do
  424. Write(Byte(Q.Payload[i]),' ');
  425. Writeln;
  426. end;
  427. Function BuildPayLoad(Var Q : TQueryData; Name : String; RR : Word; QClass : Word) : Integer;
  428. Var
  429. P : PByte;
  430. l,S : Integer;
  431. begin
  432. Result:=-1;
  433. If length(Name)>506 then
  434. Exit;
  435. Result:=0;
  436. P:[email protected];
  437. Repeat
  438. L:=Pos('.',Name);
  439. If (L=0) then
  440. S:=Length(Name)
  441. else
  442. S:=L-1;
  443. P[Result]:=S;
  444. Move(Name[1],P[Result+1],S);
  445. Inc(Result,S+1);
  446. If (L>0) then
  447. Delete(Name,1,L);
  448. Until (L=0);
  449. P[Result]:=0;
  450. rr := htons(rr);
  451. Move(rr,P[Result+1],2);
  452. Inc(Result,3);
  453. QClass := htons(QClass);
  454. Move(qclass,P[Result],2);
  455. Inc(Result,2);
  456. end;
  457. Function NextRR(Const PayLoad : TPayLoad;Var Start : LongInt; AnsLen : LongInt; Var RR : TRRData) : Boolean;
  458. Var
  459. I : Integer;
  460. HaveName : Boolean;
  461. PA : ^TRRData;
  462. begin
  463. Result:=False;
  464. I:=Start;
  465. // Skip labels and pointers. At least 1 label or pointer is present.
  466. Repeat
  467. HaveName:=True;
  468. If (Payload[i]>#63) then // Pointer, skip
  469. Inc(I,2)
  470. else If Payload[i]=#0 then // Null termination of label, skip.
  471. Inc(i)
  472. else
  473. begin
  474. Inc(I,Ord(Payload[i])+1); // Label, continue scan.
  475. HaveName:=False;
  476. end;
  477. Until HaveName or (I>(AnsLen-SizeOf(TRRData)));
  478. Result:=(I<=(AnsLen-SizeOf(TRRData)));
  479. // Check RR record.
  480. PA:=@Payload[i];
  481. RR:=PA^;
  482. Start:=I+SizeOf(TRRData);
  483. end;
  484. Function BuildName (Const PayLoad : TPayLoad; Start,len : Integer) : String;
  485. Const
  486. FIREDNS_POINTER_VALUE = $C000;
  487. Var
  488. I,O : Integer;
  489. P : Word;
  490. begin
  491. SetLength(Result,512);
  492. I:=Start;
  493. O:=1;
  494. // Copy labels and pointers. At least 1 label or pointer is present.
  495. Repeat
  496. If (Payload[i]>#63) then // Pointer, move.
  497. begin
  498. Move(Payload[i],P,2);
  499. I:=ntohs(p)-FIREDNS_POINTER_VALUE-12;
  500. end
  501. else if Payload[i]<>#0 then // Label, copy
  502. begin
  503. If O<>1 then
  504. begin
  505. Result[O]:='.';
  506. Inc(O);
  507. end;
  508. P:=Ord(Payload[i]);
  509. Move(Payload[i+1],Result[o],P);
  510. Inc(I,P+1);
  511. Inc(O,P);
  512. end;
  513. Until (Payload[I]=#0);
  514. setlength(result,o-1);
  515. end;
  516. { ---------------------------------------------------------------------
  517. QueryData handling functions
  518. ---------------------------------------------------------------------}
  519. Function CheckAnswer(Const Qry : TQueryData; Var Ans : TQueryData) : Boolean;
  520. begin
  521. Result:=False;
  522. With Ans do
  523. begin
  524. // Check ID.
  525. If (ID[1]<>QRY.ID[1]) or (ID[0]<>Qry.ID[0]) then
  526. exit;
  527. // Flags ?
  528. If (Flags1 and QF_QR)=0 then
  529. exit;
  530. if (Flags1 and QF_OPCODE)<>0 then
  531. exit;
  532. if (Flags2 and QF_RCODE)<>0 then
  533. exit;
  534. // Number of answers ?
  535. AnCount := htons(Ancount);
  536. If Ancount<1 then
  537. Exit;
  538. Result:=True;
  539. end;
  540. end;
  541. Function SkipAnsQueries(Var Ans : TQueryData; L : Integer) : integer;
  542. Var
  543. Q,I : Integer;
  544. begin
  545. Result:=0;
  546. With Ans do
  547. begin
  548. qdcount := htons(qdcount);
  549. i:=0;
  550. q:=0;
  551. While (Q<qdcount) and (i<l) do
  552. begin
  553. If Ord(Payload[i])>63 then
  554. begin
  555. Inc(I,6);
  556. Inc(Q);
  557. end
  558. else
  559. begin
  560. If Payload[i]=#0 then
  561. begin
  562. inc(q);
  563. Inc(I,5);
  564. end
  565. else
  566. Inc(I,Ord(Payload[i])+1);
  567. end;
  568. end;
  569. Result:=I;
  570. end;
  571. end;
  572. { ---------------------------------------------------------------------
  573. DNS Query functions.
  574. ---------------------------------------------------------------------}
  575. Function Query(Resolver : Integer; Var Qry,Ans : TQueryData; QryLen : Integer; Var AnsLen : Integer) : Boolean;
  576. Var
  577. SA : TInetSockAddr;
  578. Sock,L : Longint;
  579. Al,RTO : Longint;
  580. ReadFDS : TFDSet;
  581. begin
  582. Result:=False;
  583. With Qry do
  584. begin
  585. ID[0]:=Random(256);
  586. ID[1]:=Random(256);
  587. Flags1:=QF_RD;
  588. Flags2:=0;
  589. qdcount:=htons(1); // was 1 shl 8;
  590. ancount:=0;
  591. nscount:=0;
  592. arcount:=0;
  593. end;
  594. Sock:=Socket(PF_INET,SOCK_DGRAM,0);
  595. If Sock=-1 then
  596. exit;
  597. With SA do
  598. begin
  599. family:=AF_INET;
  600. port:=htons(DNSport);
  601. addr:=cardinal(DNSServers[Resolver]); // dnsservers already in net order
  602. end;
  603. sendto(sock,qry,qrylen+12,0,SA,SizeOf(SA));
  604. // Wait for answer.
  605. RTO:=TimeOutS*1000+TimeOutMS;
  606. fpFD_ZERO(ReadFDS);
  607. fpFD_Set(sock,readfds);
  608. if fpSelect(Sock+1,@readfds,Nil,Nil,RTO)<=0 then
  609. begin
  610. fpclose(Sock);
  611. exit;
  612. end;
  613. AL:=SizeOf(SA);
  614. L:=recvfrom(Sock,ans,SizeOf(Ans),0,SA,AL);
  615. fpclose(Sock);
  616. // Check lenght answer and fields in header data.
  617. If (L<12) or not CheckAnswer(Qry,Ans) Then
  618. exit;
  619. // Return Payload length.
  620. Anslen:=L-12;
  621. Result:=True;
  622. end;
  623. function stringfromlabel(pl: TPayLoad; start: integer): string;
  624. var
  625. l,i: integer;
  626. begin
  627. result := '';
  628. l := 0;
  629. i := 0;
  630. repeat
  631. l := ord(pl[start]);
  632. { compressed reply }
  633. while (l >= 192) do
  634. begin
  635. { the -12 is because of the reply header length }
  636. start := (l and not(192)) shl 8 + ord(pl[start+1]) - 12;
  637. l := ord(pl[start]);
  638. end;
  639. if l <> 0 then begin
  640. setlength(result,length(result)+l);
  641. move(pl[start+1],result[i+1],l);
  642. result := result + '.';
  643. inc(start,l); inc(start);
  644. inc(i,l); inc(i);
  645. end;
  646. until l = 0;
  647. if result[length(result)] = '.' then setlength(result,length(result)-1);
  648. end;
  649. Function ResolveNameAt(Resolver : Integer; HostName : String; Var Addresses : Array of THostAddr; Recurse: Integer) : Integer;
  650. Var
  651. Qry, Ans : TQueryData;
  652. MaxAnswer,I,QryLen,
  653. AnsLen,AnsStart : Longint;
  654. RR : TRRData;
  655. cname : string;
  656. begin
  657. Result:=0;
  658. QryLen:=BuildPayLoad(Qry,HostName,DNSQRY_A,1);
  659. If Not Query(Resolver,Qry,Ans,QryLen,AnsLen) then
  660. Result:=-1
  661. else
  662. begin
  663. AnsStart:=SkipAnsQueries(Ans,AnsLen);
  664. MaxAnswer:=Ans.AnCount-1;
  665. If MaxAnswer>High(Addresses) then
  666. MaxAnswer:=High(Addresses);
  667. I:=0;
  668. While (I<=MaxAnswer) and NextRR(Ans.Payload,AnsStart,AnsLen,RR) do
  669. begin
  670. if htons(rr.AClass) = 1 then
  671. case ntohs(rr.AType) of
  672. DNSQRY_A: begin
  673. Move(Ans.PayLoad[AnsStart],Addresses[i],SizeOf(THostAddr));
  674. inc(Result);
  675. Inc(AnsStart,htons(RR.RDLength));
  676. end;
  677. DNSQRY_CNAME: begin
  678. if Recurse >= MaxRecursion then begin
  679. Result := -1;
  680. exit;
  681. end;
  682. rr.rdlength := ntohs(rr.rdlength);
  683. setlength(cname, rr.rdlength);
  684. cname := stringfromlabel(ans.payload, ansstart);
  685. Result := ResolveNameAt(Resolver, cname, Addresses, Recurse+1);
  686. exit; // FIXME: what about other servers?!
  687. end;
  688. end;
  689. Inc(I);
  690. end;
  691. end;
  692. end;
  693. Function ResolveName(HostName : String; Var Addresses : Array of THostAddr) : Integer;
  694. Var
  695. I : Integer;
  696. begin
  697. CheckResolveFile;
  698. I:=1;
  699. Result:=0;
  700. While (Result<=0) and (I<=DNSServerCount) do
  701. begin
  702. Result:=ResolveNameAt(I,HostName,Addresses,0);
  703. Inc(I);
  704. end;
  705. end;
  706. //const NoAddress6 : array[0..7] of word = (0,0,0,0,0,0,0,0);
  707. Function ResolveNameAt6(Resolver : Integer; HostName : String; Var Addresses : Array of THostAddr6; Recurse: Integer) : Integer;
  708. Var
  709. Qry, Ans : TQueryData;
  710. MaxAnswer,I,QryLen,
  711. AnsLen,AnsStart : Longint;
  712. RR : TRRData;
  713. cname : string;
  714. LIP4mapped: array[0..MaxIP4Mapped-1] of THostAddr;
  715. LIP4count: Longint;
  716. begin
  717. Result:=0;
  718. QryLen:=BuildPayLoad(Qry,HostName,DNSQRY_AAAA,1);
  719. If Not Query(Resolver,Qry,Ans,QryLen,AnsLen) then begin
  720. // no answer? try IPv4 mapped addresses, maybe that will generate one
  721. LIP4Count := ResolveName(HostName, LIP4Mapped);
  722. if LIP4Count > 0 then begin
  723. inc(LIP4Count); // we loop to LIP4Count-1 later
  724. if LIP4Count > MaxIP4Mapped then LIP4Count := MaxIP4Mapped;
  725. if LIP4Count > Length(Addresses) then LIP4Count := Length(Addresses);
  726. for i := 0 to LIP4Count-2 do begin
  727. Addresses[i] := NoAddress6;
  728. Addresses[i].u6_addr16[5] := $FFFF;
  729. Move(LIP4Mapped[i], Addresses[i].u6_addr16[6], 4);
  730. end;
  731. Result := LIP4Count;
  732. end else begin
  733. Result:=-1
  734. end;
  735. end else
  736. begin
  737. AnsStart:=SkipAnsQueries(Ans,AnsLen);
  738. MaxAnswer:=Ans.AnCount-1;
  739. If MaxAnswer>High(Addresses) then
  740. MaxAnswer:=High(Addresses);
  741. I:=0;
  742. While (I<=MaxAnswer) and NextRR(Ans.Payload,AnsStart,AnsLen,RR) do
  743. begin
  744. if (1=NtoHS(RR.AClass)) then
  745. case ntohs(rr.atype) of
  746. DNSQRY_AAAA: begin
  747. Move(Ans.PayLoad[AnsStart],Addresses[i],SizeOf(THostAddr6));
  748. inc(Result);
  749. rr.rdlength := ntohs(rr.rdlength);
  750. Inc(AnsStart,RR.RDLength);
  751. end;
  752. DNSQRY_CNAME: begin
  753. if Recurse >= MaxRecursion then begin
  754. Result := -1;
  755. exit;
  756. end;
  757. rr.rdlength := ntohs(rr.rdlength);
  758. setlength(cname, rr.rdlength);
  759. cname := stringfromlabel(ans.payload, ansstart);
  760. Result := ResolveNameAt6(Resolver, cname, Addresses, Recurse+1);
  761. exit; // FIXME: what about other servers?!
  762. end;
  763. end;
  764. Inc(I);
  765. end;
  766. end;
  767. end;
  768. Function ResolveName6(HostName: String; Var Addresses: Array of THostAddr6) : Integer;
  769. var
  770. i: Integer;
  771. begin
  772. CheckResolveFile;
  773. i := 1;
  774. Result := 0;
  775. while (Result <= 0) and (I<= DNSServerCount) do begin
  776. Result := ResolveNameAt6(I, Hostname, Addresses, 0);
  777. Inc(i);
  778. end;
  779. end;
  780. Function ResolveAddressAt(Resolver : Integer; Address : String; Var Names : Array of String) : Integer;
  781. Var
  782. Qry, Ans : TQueryData;
  783. MaxAnswer,I,QryLen,
  784. AnsLen,AnsStart : Longint;
  785. RR : TRRData;
  786. begin
  787. Result:=0;
  788. QryLen:=BuildPayLoad(Qry,Address,DNSQRY_PTR,1);
  789. If Not Query(Resolver,Qry,Ans,QryLen,AnsLen) then
  790. Result:=-1
  791. else
  792. begin
  793. AnsStart:=SkipAnsQueries(Ans,AnsLen);
  794. MaxAnswer:=Ans.AnCount-1;
  795. If MaxAnswer>High(Names) then
  796. MaxAnswer:=High(Names);
  797. I:=0;
  798. While (I<=MaxAnswer) and NextRR(Ans.Payload,AnsStart,AnsLen,RR) do
  799. begin
  800. if (Ntohs(RR.AType)=DNSQRY_PTR) and (1=NtoHS(RR.AClass)) then
  801. begin
  802. Names[i]:=BuildName(Ans.Payload,AnsStart,AnsLen);
  803. inc(Result);
  804. RR.RDLength := ntohs(RR.RDLength);
  805. Inc(AnsStart,RR.RDLength);
  806. end;
  807. Inc(I);
  808. end;
  809. end;
  810. end;
  811. Function ResolveAddress(HostAddr : THostAddr; Var Addresses : Array of String) : Integer;
  812. Var
  813. I : Integer;
  814. S : String;
  815. nt : tnetaddr;
  816. begin
  817. CheckResolveFile;
  818. I:=1;
  819. Result:=0;
  820. nt:=hosttonet(hostaddr);
  821. S:=Format('%d.%d.%d.%d.in-addr.arpa',[nt.s_bytes[4],nt.s_bytes[3],nt.s_bytes[2],nt.s_bytes[1]]);
  822. While (Result=0) and (I<=DNSServerCount) do
  823. begin
  824. Result:=ResolveAddressAt(I,S,Addresses);
  825. Inc(I);
  826. end;
  827. end;
  828. Function ResolveAddress6(HostAddr : THostAddr6; Var Addresses : Array of String) : Integer;
  829. const
  830. hexdig: string[16] = '0123456789abcdef';
  831. Var
  832. I : Integer;
  833. S : ShortString;
  834. begin
  835. CheckResolveFile;
  836. Result:=0;
  837. S := '0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.int';
  838. for i := 7 downto 0 do begin
  839. S[5+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $000F) shr 00];
  840. S[7+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $00F0) shr 04];
  841. S[1+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $0F00) shr 08];
  842. S[3+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $F000) shr 12];
  843. end;
  844. I := 1;
  845. While (Result=0) and (I<=DNSServerCount) do
  846. begin
  847. Result:=ResolveAddressAt(I,S,Addresses);
  848. Inc(I);
  849. end;
  850. end;
  851. function IN6_IS_ADDR_V4MAPPED(HostAddr: THostAddr6): boolean;
  852. begin
  853. Result :=
  854. (HostAddr.u6_addr16[0] = 0) and
  855. (HostAddr.u6_addr16[1] = 0) and
  856. (HostAddr.u6_addr16[2] = 0) and
  857. (HostAddr.u6_addr16[3] = 0) and
  858. (HostAddr.u6_addr16[4] = 0) and
  859. (HostAddr.u6_addr16[5] = $FFFF);
  860. end;
  861. Function ResolveHostByName(HostName : String; Var H : THostEntry) : Boolean;
  862. Var
  863. Address : Array[1..MaxResolveAddr] of THostAddr;
  864. L : Integer;
  865. begin
  866. L:=ResolveName(HostName,Address);
  867. Result:=(L>0);
  868. If Result then
  869. begin
  870. // We could add a reverse call here to get the real name and aliases.
  871. H.Name:=HostName;
  872. H.Addr:=Address[1];
  873. H.aliases:='';
  874. end;
  875. end;
  876. Function ResolveHostByAddr(HostAddr : THostAddr; Var H : THostEntry) : Boolean;
  877. Var
  878. Names : Array[1..MaxResolveAddr] of String;
  879. I,L : Integer;
  880. begin
  881. L:=ResolveAddress(HostAddr,Names);
  882. Result:=(L>0);
  883. If Result then
  884. begin
  885. H.Name:=Names[1];
  886. H.Addr:=HostAddr;
  887. H.Aliases:='';
  888. If (L>1) then
  889. For I:=2 to L do
  890. If (I=2) then
  891. H.Aliases:=Names[i]
  892. else
  893. H.Aliases:=H.Aliases+','+Names[i];
  894. end;
  895. end;
  896. //const NoAddress : in_addr = (s_addr: 0);
  897. Function GetHostByName(HostName: String; Var H : THostEntry) : boolean;
  898. begin
  899. Result:=FindHostEntryInHostsFile(HostName,NoAddress,H);
  900. end;
  901. Function GetHostByAddr(Addr: THostAddr; Var H : THostEntry) : boolean;
  902. begin
  903. Result:=FindHostEntryInHostsFile('',Addr,H);
  904. end;
  905. { ---------------------------------------------------------------------
  906. /etc/protocols handling.
  907. ---------------------------------------------------------------------}
  908. Function GetNextProtoEntry(var F : Text; Var H : TProtocolEntry): boolean;
  909. Var
  910. Line,S : String;
  911. I : integer;
  912. begin
  913. Result:=False;
  914. Repeat
  915. ReadLn(F,Line);
  916. StripComment(Line);
  917. S:=NextWord(Line);
  918. If (S<>'') then
  919. begin
  920. H.Name:=S;
  921. S:=NextWord(Line);
  922. i:=strtointdef(s,-1);
  923. If (i<>-1) then
  924. begin
  925. H.number:=i;
  926. Result:=True;
  927. H.Aliases:='';
  928. Repeat
  929. S:=NextWord(line);
  930. If (S<>'') then
  931. If (H.Aliases='') then
  932. H.Aliases:=S
  933. else
  934. H.Aliases:=H.Aliases+','+S;
  935. until (S='');
  936. end;
  937. end;
  938. until Result or EOF(F);
  939. end;
  940. Function FindProtoEntryInProtoFile(N: String; prot: integer; Var H : TProtocolEntry) : boolean;
  941. Var
  942. F : Text;
  943. HE : TProtocolEntry;
  944. begin
  945. Result:=False;
  946. If FileExists(SProtocolFile) then
  947. begin
  948. Assign(F,SProtocolFile);
  949. {$i-}
  950. Reset(F);
  951. {$i+}
  952. If (IOResult=0) then
  953. begin
  954. While Not Result and GetNextProtoEntry(F,HE) do
  955. begin
  956. If (N<>'') then
  957. Result:=MatchNameOrAlias(N,HE.Name,HE.Aliases)
  958. else
  959. Result:=prot=he.number;
  960. end;
  961. Close(f);
  962. If Result then
  963. begin
  964. H.Name:=HE.Name;
  965. H.number:=he.number;
  966. H.Aliases:=HE.Aliases;
  967. end;
  968. end;
  969. end;
  970. end;
  971. Function GetProtocolByName(ProtoName: String; Var H : TProtocolEntry) : boolean;
  972. begin
  973. Result:=FindProtoEntryInProtoFile(ProtoName,0,H);
  974. end;
  975. Function GetProtocolByNumber(proto: Integer; Var H : TProtocolEntry) : boolean;
  976. begin
  977. Result:=FindProtoEntryInProtoFile('',Proto,H);
  978. end;
  979. { ---------------------------------------------------------------------
  980. /etc/networks handling
  981. ---------------------------------------------------------------------}
  982. function StrTonetpartial( IP : AnsiString) : in_addr ;
  983. Var
  984. Dummy : AnsiString;
  985. I,j,k : Longint;
  986. Temp : in_addr;
  987. begin
  988. strtonetpartial.s_addr:=0; //:=NoAddress;
  989. i:=0; j:=0;
  990. while (i<4) and (j=0) do
  991. begin
  992. J:=Pos('.',IP);
  993. if j=0 then j:=length(ip)+1;
  994. Dummy:=Copy(IP,1,J-1);
  995. Delete (IP,1,J);
  996. Val (Dummy,k,J);
  997. if j=0 then
  998. strtonetpartial.s_bytes[i+1]:=k;
  999. inc(i);
  1000. end;
  1001. if (i=0) then strtonetpartial.s_addr:=0;
  1002. end;
  1003. Function GetNextNetworkEntry(var F : Text; Var N : TNetworkEntry): boolean;
  1004. Var
  1005. NN,Line,S : String;
  1006. A : TNetAddr;
  1007. begin
  1008. Result:=False;
  1009. Repeat
  1010. ReadLn(F,Line);
  1011. StripComment(Line);
  1012. S:=NextWord(Line);
  1013. If (S<>'') then
  1014. begin
  1015. NN:=S;
  1016. A:=StrTonetpartial(NextWord(Line));
  1017. Result:=(NN<>'') and (A.s_bytes[1]<>0); // Valid addr.
  1018. If result then
  1019. begin
  1020. N.Addr.s_addr:=A.s_addr; // keep it host.
  1021. N.Name:=NN;
  1022. N.Aliases:='';
  1023. end;
  1024. end;
  1025. until Result or EOF(F);
  1026. end;
  1027. Function FindNetworkEntryInNetworksFile(Net: String; Addr: TNetAddr; Var N : TNetworkEntry) : boolean;
  1028. Var
  1029. F : Text;
  1030. NE : TNetworkEntry;
  1031. begin
  1032. Result:=False;
  1033. If FileExists(SNetworksFile) then
  1034. begin
  1035. Assign(F,SNetworksFile);
  1036. {$i-}
  1037. Reset(F);
  1038. {$i+}
  1039. If (IOResult=0) then
  1040. begin
  1041. While Not Result and GetNextNetworkEntry(F,NE) do
  1042. begin
  1043. If (Net<>'') then
  1044. Result:=MatchNameOrAlias(Net,NE.Name,NE.Aliases)
  1045. else
  1046. Result:=Cardinal(Addr)=Cardinal(NE.Addr);
  1047. end;
  1048. Close(f);
  1049. If Result then
  1050. begin
  1051. N.Name:=NE.Name;
  1052. N.Addr:=nettohost(NE.Addr);
  1053. N.Aliases:=NE.Aliases;
  1054. end;
  1055. end;
  1056. end;
  1057. end;
  1058. Const NoNet : in_addr = (s_addr:0);
  1059. Function GetNetworkByName(NetName: String; Var N : TNetworkEntry) : boolean;
  1060. begin
  1061. Result:=FindNetworkEntryInNetworksFile(NetName,NoNet,N);
  1062. end;
  1063. Function GetNetworkByAddr(Addr: THostAddr; Var N : TNetworkEntry) : boolean;
  1064. begin
  1065. Result:=FindNetworkEntryInNetworksFile('',Addr,N);
  1066. end;
  1067. { ---------------------------------------------------------------------
  1068. /etc/services section
  1069. ---------------------------------------------------------------------}
  1070. Function GetNextServiceEntry(Var F : Text; Var E : TServiceEntry) : Boolean;
  1071. Var
  1072. Line,S : String;
  1073. P : INteger;
  1074. begin
  1075. Result:=False;
  1076. Repeat
  1077. ReadLn(F,Line);
  1078. StripComment(Line);
  1079. S:=NextWord(Line);
  1080. If (S<>'') then
  1081. begin
  1082. E.Name:=S;
  1083. S:=NextWord(Line);
  1084. P:=Pos('/',S);
  1085. If (P<>0) then
  1086. begin
  1087. E.Port:=StrToIntDef(Copy(S,1,P-1),0);
  1088. If (E.Port<>0) then
  1089. begin
  1090. E.Protocol:=Copy(S,P+1,Length(S)-P);
  1091. Result:=length(E.Protocol)>0;
  1092. E.Aliases:='';
  1093. Repeat
  1094. S:=NextWord(Line);
  1095. If (S<>'') then
  1096. If (Length(E.Aliases)=0) then
  1097. E.aliases:=S
  1098. else
  1099. E.Aliases:=E.Aliases+','+S;
  1100. until (S='');
  1101. end;
  1102. end;
  1103. end;
  1104. until Result or EOF(F);
  1105. end;
  1106. Function FindServiceEntryInFile(Const Name,Proto : String; Port : Integer; Var E : TServiceEntry) : Boolean;
  1107. Var
  1108. F : Text;
  1109. TE : TServiceEntry;
  1110. begin
  1111. Result:=False;
  1112. If FileExists(SServicesFile) then
  1113. begin
  1114. Assign(F,SServicesFile);
  1115. {$i-}
  1116. Reset(F);
  1117. {$i+}
  1118. If (IOResult=0) then
  1119. begin
  1120. While Not Result and GetNextServiceEntry(F,TE) do
  1121. begin
  1122. If (Port=-1) then
  1123. Result:=MatchNameOrAlias(Name,TE.Name,TE.Aliases)
  1124. else
  1125. Result:=(Port=TE.Port);
  1126. If Result and (Proto<>'') then
  1127. Result:=(Proto=TE.Protocol);
  1128. end;
  1129. Close(f);
  1130. If Result then
  1131. begin
  1132. E.Name:=TE.Name;
  1133. E.Port:=TE.Port;
  1134. E.Protocol:=TE.Protocol;
  1135. E.Aliases:=TE.Aliases;
  1136. end;
  1137. end;
  1138. end;
  1139. end;
  1140. Function GetServiceByName(Const Name,Proto : String; Var E : TServiceEntry) : Boolean;
  1141. begin
  1142. Result:=FindServiceEntryInFile(Name,Proto,-1,E);
  1143. end;
  1144. Function GetServiceByPort(Port : Word;Const Proto : String; Var E : TServiceEntry) : Boolean;
  1145. begin
  1146. Result:=FindServiceEntryInFile('',Proto,Port,E);
  1147. end;
  1148. { ---------------------------------------------------------------------
  1149. Initialization section
  1150. ---------------------------------------------------------------------}
  1151. Procedure InitResolver;
  1152. Var
  1153. I : Integer;
  1154. begin
  1155. TimeOutS :=5;
  1156. TimeOutMS:=0;
  1157. CheckHostsFileAge:=False;
  1158. If FileExists(SHostsFile) then
  1159. HostsList:=ProcessHosts(SHostsFile);
  1160. CheckResolveFileAge:=False;
  1161. If FileExists(SResolveFile) then
  1162. GetDNsservers(SResolveFile);
  1163. end;
  1164. Procedure DoneResolver;
  1165. begin
  1166. FreeHostsList(HostsList);
  1167. end;
  1168. Initialization
  1169. InitResolver;
  1170. Finalization
  1171. DoneResolver;
  1172. end.