netdb.pp 27 KB

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