netdb.pp 29 KB

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