netdb.pp 28 KB

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