netdb.pp 33 KB

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