netdb.pp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  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. end;
  303. { ---------------------------------------------------------------------
  304. QueryData handling functions
  305. ---------------------------------------------------------------------}
  306. Function CheckAnswer(Const Qry : TQueryData; Var Ans : TQueryData) : Boolean;
  307. begin
  308. Result:=False;
  309. With Ans do
  310. begin
  311. // Check ID.
  312. If (ID[1]<>QRY.ID[1]) or (ID[0]<>Qry.ID[0]) then
  313. exit;
  314. // Flags ?
  315. If (Flags1 and QF_QR)=0 then
  316. exit;
  317. if (Flags1 and QF_OPCODE)<>0 then
  318. exit;
  319. if (Flags2 and QF_RCODE)<>0 then
  320. exit;
  321. // Number of answers ?
  322. AnCount := htons(Ancount);
  323. If Ancount<1 then
  324. Exit;
  325. Result:=True;
  326. end;
  327. end;
  328. Function SkipAnsQueries(Var Ans : TQueryData; L : Integer) : integer;
  329. Var
  330. Q,I : Integer;
  331. begin
  332. Result:=0;
  333. With Ans do
  334. begin
  335. qdcount := htons(qdcount);
  336. i:=0;
  337. q:=0;
  338. While (Q<qdcount) and (i<l) do
  339. begin
  340. If Ord(Payload[i])>63 then
  341. begin
  342. Inc(I,6);
  343. Inc(Q);
  344. end
  345. else
  346. begin
  347. If Payload[i]=#0 then
  348. begin
  349. inc(q);
  350. Inc(I,5);
  351. end
  352. else
  353. Inc(I,Ord(Payload[i])+1);
  354. end;
  355. end;
  356. Result:=I;
  357. end;
  358. end;
  359. { ---------------------------------------------------------------------
  360. DNS Query functions.
  361. ---------------------------------------------------------------------}
  362. Function Query(Resolver : Integer; Var Qry,Ans : TQueryData; QryLen : Integer; Var AnsLen : Integer) : Boolean;
  363. Var
  364. SA : TInetSockAddr;
  365. Sock,L : Longint;
  366. Al,RTO : Longint;
  367. ReadFDS : TFDSet;
  368. begin
  369. Result:=False;
  370. With Qry do
  371. begin
  372. ID[0]:=Random(256);
  373. ID[1]:=Random(256);
  374. Flags1:=QF_RD;
  375. Flags2:=0;
  376. qdcount:=htons(1); // was 1 shl 8;
  377. ancount:=0;
  378. nscount:=0;
  379. arcount:=0;
  380. end;
  381. Sock:=Socket(PF_INET,SOCK_DGRAM,0);
  382. If Sock=-1 then
  383. exit;
  384. With SA do
  385. begin
  386. family:=AF_INET;
  387. port:=htons(DNSport);
  388. addr:=cardinal(DNSServers[Resolver]); // dnsservers already in net order
  389. end;
  390. sendto(sock,qry,qrylen+12,0,SA,SizeOf(SA));
  391. // Wait for answer.
  392. RTO:=TimeOutS*1000+TimeOutMS;
  393. fpFD_ZERO(ReadFDS);
  394. fpFD_Set(sock,readfds);
  395. if fpSelect(Sock+1,@readfds,Nil,Nil,RTO)<=0 then
  396. begin
  397. fpclose(Sock);
  398. exit;
  399. end;
  400. AL:=SizeOf(SA);
  401. L:=recvfrom(Sock,ans,SizeOf(Ans),0,SA,AL);
  402. fpclose(Sock);
  403. // Check lenght answer and fields in header data.
  404. If (L<12) or not CheckAnswer(Qry,Ans) Then
  405. exit;
  406. // Return Payload length.
  407. Anslen:=L-12;
  408. Result:=True;
  409. end;
  410. function stringfromlabel(pl: TPayLoad; start: integer): string;
  411. var
  412. l,i: integer;
  413. begin
  414. result := '';
  415. l := 0;
  416. i := 0;
  417. repeat
  418. l := ord(pl[start]);
  419. if l <> 0 then begin
  420. setlength(result,length(result)+l);
  421. move(pl[start+1],result[i+1],l);
  422. result := result + '.';
  423. inc(start,l); inc(start);
  424. inc(i,l); inc(i);
  425. end;
  426. until l = 0;
  427. if result[length(result)] = '.' then setlength(result,length(result)-1);
  428. end;
  429. Function ResolveNameAt(Resolver : Integer; HostName : String; Var Addresses : Array of THostAddr; Recurse: Integer) : Integer;
  430. Var
  431. Qry, Ans : TQueryData;
  432. MaxAnswer,I,QryLen,
  433. AnsLen,AnsStart : Longint;
  434. RR : TRRData;
  435. cname : string;
  436. begin
  437. Result:=0;
  438. QryLen:=BuildPayLoad(Qry,HostName,DNSQRY_A,1);
  439. If Not Query(Resolver,Qry,Ans,QryLen,AnsLen) then
  440. Result:=-1
  441. else
  442. begin
  443. AnsStart:=SkipAnsQueries(Ans,AnsLen);
  444. MaxAnswer:=Ans.AnCount-1;
  445. If MaxAnswer>High(Addresses) then
  446. MaxAnswer:=High(Addresses);
  447. I:=0;
  448. While (I<=MaxAnswer) and NextRR(Ans.Payload,AnsStart,AnsLen,RR) do
  449. begin
  450. if htons(rr.AClass) = 1 then
  451. case ntohs(rr.AType) of
  452. DNSQRY_A: begin
  453. Move(Ans.PayLoad[AnsStart],Addresses[i],SizeOf(THostAddr));
  454. inc(Result);
  455. Inc(AnsStart,htons(RR.RDLength));
  456. end;
  457. DNSQRY_CNAME: begin
  458. if Recurse >= MaxRecursion then begin
  459. Result := -1;
  460. exit;
  461. end;
  462. rr.rdlength := ntohs(rr.rdlength);
  463. setlength(cname, rr.rdlength);
  464. cname := stringfromlabel(ans.payload, ansstart);
  465. Result := ResolveNameAt(Resolver, cname, Addresses, Recurse+1);
  466. exit; // FIXME: what about other servers?!
  467. end;
  468. end;
  469. Inc(I);
  470. end;
  471. end;
  472. end;
  473. Function ResolveName(HostName : String; Var Addresses : Array of THostAddr) : Integer;
  474. Var
  475. I : Integer;
  476. begin
  477. CheckResolveFile;
  478. I:=1;
  479. Result:=0;
  480. While (Result<=0) and (I<=DNSServerCount) do
  481. begin
  482. Result:=ResolveNameAt(I,HostName,Addresses,0);
  483. Inc(I);
  484. end;
  485. end;
  486. //const NoAddress6 : array[0..7] of word = (0,0,0,0,0,0,0,0);
  487. Function ResolveNameAt6(Resolver : Integer; HostName : String; Var Addresses : Array of THostAddr6; Recurse: Integer) : Integer;
  488. Var
  489. Qry, Ans : TQueryData;
  490. MaxAnswer,I,QryLen,
  491. AnsLen,AnsStart : Longint;
  492. RR : TRRData;
  493. cname : string;
  494. LIP4mapped: array[0..MaxIP4Mapped-1] of THostAddr;
  495. LIP4count: Longint;
  496. begin
  497. Result:=0;
  498. QryLen:=BuildPayLoad(Qry,HostName,DNSQRY_AAAA,1);
  499. If Not Query(Resolver,Qry,Ans,QryLen,AnsLen) then begin
  500. // no answer? try IPv4 mapped addresses, maybe that will generate one
  501. LIP4Count := ResolveName(HostName, LIP4Mapped);
  502. if LIP4Count > 0 then begin
  503. inc(LIP4Count); // we loop to LIP4Count-1 later
  504. if LIP4Count > MaxIP4Mapped then LIP4Count := MaxIP4Mapped;
  505. if LIP4Count > Length(Addresses) then LIP4Count := Length(Addresses);
  506. for i := 0 to LIP4Count-2 do begin
  507. Addresses[i] := NoAddress6;
  508. Addresses[i].u6_addr16[5] := $FFFF;
  509. Move(LIP4Mapped[i], Addresses[i].u6_addr16[6], 4);
  510. end;
  511. Result := LIP4Count;
  512. end else begin
  513. Result:=-1
  514. end;
  515. end else
  516. begin
  517. AnsStart:=SkipAnsQueries(Ans,AnsLen);
  518. MaxAnswer:=Ans.AnCount-1;
  519. If MaxAnswer>High(Addresses) then
  520. MaxAnswer:=High(Addresses);
  521. I:=0;
  522. While (I<=MaxAnswer) and NextRR(Ans.Payload,AnsStart,AnsLen,RR) do
  523. begin
  524. if (1=NtoHS(RR.AClass)) then
  525. case ntohs(rr.atype) of
  526. DNSQRY_AAAA: begin
  527. Move(Ans.PayLoad[AnsStart],Addresses[i],SizeOf(THostAddr6));
  528. inc(Result);
  529. rr.rdlength := ntohs(rr.rdlength);
  530. Inc(AnsStart,RR.RDLength);
  531. end;
  532. DNSQRY_CNAME: begin
  533. if Recurse >= MaxRecursion then begin
  534. Result := -1;
  535. exit;
  536. end;
  537. rr.rdlength := ntohs(rr.rdlength);
  538. setlength(cname, rr.rdlength);
  539. cname := stringfromlabel(ans.payload, ansstart);
  540. Result := ResolveNameAt6(Resolver, cname, Addresses, Recurse+1);
  541. exit; // FIXME: what about other servers?!
  542. end;
  543. end;
  544. Inc(I);
  545. end;
  546. end;
  547. end;
  548. Function ResolveName6(HostName: String; Var Addresses: Array of THostAddr6) : Integer;
  549. var
  550. i: Integer;
  551. begin
  552. CheckResolveFile;
  553. i := 1;
  554. Result := 0;
  555. while (Result <= 0) and (I<= DNSServerCount) do begin
  556. Result := ResolveNameAt6(I, Hostname, Addresses, 0);
  557. Inc(i);
  558. end;
  559. end;
  560. Function ResolveAddressAt(Resolver : Integer; Address : String; Var Names : Array of String) : Integer;
  561. Var
  562. Qry, Ans : TQueryData;
  563. MaxAnswer,I,QryLen,
  564. AnsLen,AnsStart : Longint;
  565. RR : TRRData;
  566. begin
  567. Result:=0;
  568. QryLen:=BuildPayLoad(Qry,Address,DNSQRY_PTR,1);
  569. If Not Query(Resolver,Qry,Ans,QryLen,AnsLen) then
  570. Result:=-1
  571. else
  572. begin
  573. AnsStart:=SkipAnsQueries(Ans,AnsLen);
  574. MaxAnswer:=Ans.AnCount-1;
  575. If MaxAnswer>High(Names) then
  576. MaxAnswer:=High(Names);
  577. I:=0;
  578. While (I<=MaxAnswer) and NextRR(Ans.Payload,AnsStart,AnsLen,RR) do
  579. begin
  580. if (Ntohs(RR.AType)=DNSQRY_PTR) and (1=NtoHS(RR.AClass)) then
  581. begin
  582. Names[i]:=BuildName(Ans.Payload,AnsStart,AnsLen);
  583. inc(Result);
  584. RR.RDLength := ntohs(RR.RDLength);
  585. Inc(AnsStart,RR.RDLength);
  586. end;
  587. Inc(I);
  588. end;
  589. end;
  590. end;
  591. Function ResolveAddress(HostAddr : THostAddr; Var Addresses : Array of String) : Integer;
  592. Var
  593. I : Integer;
  594. S : String;
  595. nt : tnetaddr;
  596. begin
  597. CheckResolveFile;
  598. I:=1;
  599. Result:=0;
  600. nt:=hosttonet(hostaddr);
  601. 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]]);
  602. While (Result=0) and (I<=DNSServerCount) do
  603. begin
  604. Result:=ResolveAddressAt(I,S,Addresses);
  605. Inc(I);
  606. end;
  607. end;
  608. Function ResolveAddress6(HostAddr : THostAddr6; Var Addresses : Array of String) : Integer;
  609. const
  610. hexdig: string[16] = '0123456789abcdef';
  611. Var
  612. I : Integer;
  613. S : ShortString;
  614. begin
  615. CheckResolveFile;
  616. Result:=0;
  617. 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';
  618. for i := 7 downto 0 do begin
  619. S[5+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $000F) shr 00];
  620. S[7+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $00F0) shr 04];
  621. S[1+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $0F00) shr 08];
  622. S[3+(7-i)*8] := hexdig[1+(HostAddr.u6_addr16[i] and $F000) shr 12];
  623. end;
  624. I := 1;
  625. While (Result=0) and (I<=DNSServerCount) do
  626. begin
  627. Result:=ResolveAddressAt(I,S,Addresses);
  628. Inc(I);
  629. end;
  630. end;
  631. function IN6_IS_ADDR_V4MAPPED(HostAddr: THostAddr6): boolean;
  632. begin
  633. Result :=
  634. (HostAddr.u6_addr16[0] = 0) and
  635. (HostAddr.u6_addr16[1] = 0) and
  636. (HostAddr.u6_addr16[2] = 0) and
  637. (HostAddr.u6_addr16[3] = 0) and
  638. (HostAddr.u6_addr16[4] = 0) and
  639. (HostAddr.u6_addr16[5] = $FFFF);
  640. end;
  641. Function ResolveHostByName(HostName : String; Var H : THostEntry) : Boolean;
  642. Var
  643. Address : Array[1..MaxResolveAddr] of THostAddr;
  644. L : Integer;
  645. begin
  646. L:=ResolveName(HostName,Address);
  647. Result:=(L>0);
  648. If Result then
  649. begin
  650. // We could add a reverse call here to get the real name and aliases.
  651. H.Name:=HostName;
  652. H.Addr:=Address[1];
  653. H.aliases:='';
  654. end;
  655. end;
  656. Function ResolveHostByAddr(HostAddr : THostAddr; Var H : THostEntry) : Boolean;
  657. Var
  658. Names : Array[1..MaxResolveAddr] of String;
  659. I,L : Integer;
  660. begin
  661. L:=ResolveAddress(HostAddr,Names);
  662. Result:=(L>0);
  663. If Result then
  664. begin
  665. H.Name:=Names[1];
  666. H.Addr:=HostAddr;
  667. H.Aliases:='';
  668. If (L>1) then
  669. For I:=2 to L do
  670. If (I=2) then
  671. H.Aliases:=Names[i]
  672. else
  673. H.Aliases:=H.Aliases+','+Names[i];
  674. end;
  675. end;
  676. { ---------------------------------------------------------------------
  677. Some Parsing routines
  678. ---------------------------------------------------------------------}
  679. Const
  680. Whitespace = [' ',#9];
  681. Function NextWord(Var Line : String) : String;
  682. Var
  683. I,J : Integer;
  684. begin
  685. I:=1;
  686. While (I<=Length(Line)) and (Line[i] in Whitespace) do
  687. inc(I);
  688. J:=I;
  689. While (J<=Length(Line)) and Not (Line[J] in WhiteSpace) do
  690. inc(j);
  691. Result:=Copy(Line,I,J-I);
  692. Delete(Line,1,J);
  693. end;
  694. Procedure StripComment(Var line : String);
  695. Var
  696. P : Integer;
  697. begin
  698. P:=Pos('#',Line);
  699. If (P<>0) then
  700. Line:=Trim(Copy(Line,1,P-1));
  701. end;
  702. Function MatchNameOrAlias(Const Entry,Name: String; Aliases : String) : Boolean;
  703. Var
  704. P : Integer;
  705. A : String;
  706. begin
  707. Result:=CompareText(Entry,Name)=0;
  708. If Not Result then
  709. While (Not Result) and (Length(Aliases)>0) do
  710. begin
  711. P:=Pos(',',Aliases);
  712. If (P=0) then
  713. P:=Length(Aliases)+1;
  714. A:=Copy(Aliases,1,P-1);
  715. Delete(Aliases,1,P);
  716. Result:=CompareText(A,Entry)=0;
  717. end;
  718. end;
  719. { ---------------------------------------------------------------------
  720. /etc/hosts handling.
  721. ---------------------------------------------------------------------}
  722. Function GetNextHostEntry(var F : Text; Var H : THostEntry): boolean;
  723. Var
  724. Line,S : String;
  725. begin
  726. Result:=False;
  727. Repeat
  728. ReadLn(F,Line);
  729. StripComment(Line);
  730. S:=NextWord(Line);
  731. If (S<>'') then
  732. begin
  733. H.Addr:=StrTonetAddr(S); // endianness problem here. (fixed)
  734. if (H.Addr.s_bytes[1]<>0) then
  735. begin
  736. S:=NextWord(Line);
  737. If (S<>'') then
  738. begin
  739. H.Name:=S;
  740. Result:=True;
  741. H.Aliases:='';
  742. Repeat
  743. S:=NextWord(line);
  744. If (S<>'') then
  745. If (H.Aliases='') then
  746. H.Aliases:=S
  747. else
  748. H.Aliases:=H.Aliases+','+S;
  749. until (S='');
  750. end;
  751. end;
  752. end;
  753. until Result or EOF(F);
  754. end;
  755. Function FindHostEntryInHostsFile(N: String; Addr: THostAddr; Var H : THostEntry) : boolean;
  756. Var
  757. F : Text;
  758. HE : THostEntry;
  759. begin
  760. Result:=False;
  761. If FileExists(SHostsFile) then
  762. begin
  763. Assign(F,SHostsFile);
  764. {$i-}
  765. Reset(F);
  766. {$i+}
  767. If (IOResult=0) then
  768. begin
  769. While Not Result and GetNextHostEntry(F,HE) do
  770. begin
  771. If (N<>'') then
  772. Result:=MatchNameOrAlias(N,HE.Name,HE.Aliases)
  773. else
  774. Result:=Cardinal(hosttonet(Addr))=Cardinal(HE.Addr);
  775. end;
  776. Close(f);
  777. If Result then
  778. begin
  779. H.Name:=HE.Name;
  780. H.Addr:=nettohost(HE.Addr);
  781. H.Aliases:=HE.Aliases;
  782. end;
  783. end;
  784. end;
  785. end;
  786. //const NoAddress : in_addr = (s_addr: 0);
  787. Function GetHostByName(HostName: String; Var H : THostEntry) : boolean;
  788. begin
  789. Result:=FindHostEntryInHostsFile(HostName,NoAddress,H);
  790. end;
  791. Function GetHostByAddr(Addr: THostAddr; Var H : THostEntry) : boolean;
  792. begin
  793. Result:=FindHostEntryInHostsFile('',Addr,H);
  794. end;
  795. { ---------------------------------------------------------------------
  796. /etc/protocols handling.
  797. ---------------------------------------------------------------------}
  798. Function GetNextProtoEntry(var F : Text; Var H : TProtocolEntry): boolean;
  799. Var
  800. Line,S : String;
  801. I : integer;
  802. begin
  803. Result:=False;
  804. Repeat
  805. ReadLn(F,Line);
  806. StripComment(Line);
  807. S:=NextWord(Line);
  808. If (S<>'') then
  809. begin
  810. H.Name:=S;
  811. S:=NextWord(Line);
  812. i:=strtointdef(s,-1);
  813. If (i<>-1) then
  814. begin
  815. H.number:=i;
  816. Result:=True;
  817. H.Aliases:='';
  818. Repeat
  819. S:=NextWord(line);
  820. If (S<>'') then
  821. If (H.Aliases='') then
  822. H.Aliases:=S
  823. else
  824. H.Aliases:=H.Aliases+','+S;
  825. until (S='');
  826. end;
  827. end;
  828. until Result or EOF(F);
  829. end;
  830. Function FindProtoEntryInProtoFile(N: String; prot: integer; Var H : TProtocolEntry) : boolean;
  831. Var
  832. F : Text;
  833. HE : TProtocolEntry;
  834. begin
  835. Result:=False;
  836. If FileExists(SProtocolFile) then
  837. begin
  838. Assign(F,SProtocolFile);
  839. {$i-}
  840. Reset(F);
  841. {$i+}
  842. If (IOResult=0) then
  843. begin
  844. While Not Result and GetNextProtoEntry(F,HE) do
  845. begin
  846. If (N<>'') then
  847. Result:=MatchNameOrAlias(N,HE.Name,HE.Aliases)
  848. else
  849. Result:=prot=he.number;
  850. end;
  851. Close(f);
  852. If Result then
  853. begin
  854. H.Name:=HE.Name;
  855. H.number:=he.number;
  856. H.Aliases:=HE.Aliases;
  857. end;
  858. end;
  859. end;
  860. end;
  861. Function GetProtocolByName(ProtoName: String; Var H : TProtocolEntry) : boolean;
  862. begin
  863. Result:=FindProtoEntryInProtoFile(ProtoName,0,H);
  864. end;
  865. Function GetProtocolByNumber(proto: Integer; Var H : TProtocolEntry) : boolean;
  866. begin
  867. Result:=FindProtoEntryInProtoFile('',Proto,H);
  868. end;
  869. { ---------------------------------------------------------------------
  870. /etc/networks handling
  871. ---------------------------------------------------------------------}
  872. function StrTonetpartial( IP : AnsiString) : in_addr ;
  873. Var
  874. Dummy : AnsiString;
  875. I,j,k : Longint;
  876. Temp : in_addr;
  877. begin
  878. strtonetpartial.s_addr:=0; //:=NoAddress;
  879. i:=0; j:=0;
  880. while (i<4) and (j=0) do
  881. begin
  882. J:=Pos('.',IP);
  883. if j=0 then j:=length(ip)+1;
  884. Dummy:=Copy(IP,1,J-1);
  885. Delete (IP,1,J);
  886. Val (Dummy,k,J);
  887. if j=0 then
  888. strtonetpartial.s_bytes[i+1]:=k;
  889. inc(i);
  890. end;
  891. if (i=0) then strtonetpartial.s_addr:=0;
  892. end;
  893. Function GetNextNetworkEntry(var F : Text; Var N : TNetworkEntry): boolean;
  894. Var
  895. NN,Line,S : String;
  896. A : TNetAddr;
  897. begin
  898. Result:=False;
  899. Repeat
  900. ReadLn(F,Line);
  901. StripComment(Line);
  902. S:=NextWord(Line);
  903. If (S<>'') then
  904. begin
  905. NN:=S;
  906. A:=StrTonetpartial(NextWord(Line));
  907. Result:=(NN<>'') and (A.s_bytes[1]<>0); // Valid addr.
  908. If result then
  909. begin
  910. N.Addr.s_addr:=A.s_addr; // keep it host.
  911. N.Name:=NN;
  912. N.Aliases:='';
  913. end;
  914. end;
  915. until Result or EOF(F);
  916. end;
  917. Function FindNetworkEntryInNetworksFile(Net: String; Addr: TNetAddr; Var N : TNetworkEntry) : boolean;
  918. Var
  919. F : Text;
  920. NE : TNetworkEntry;
  921. begin
  922. Result:=False;
  923. If FileExists(SNetworksFile) then
  924. begin
  925. Assign(F,SNetworksFile);
  926. {$i-}
  927. Reset(F);
  928. {$i+}
  929. If (IOResult=0) then
  930. begin
  931. While Not Result and GetNextNetworkEntry(F,NE) do
  932. begin
  933. If (Net<>'') then
  934. Result:=MatchNameOrAlias(Net,NE.Name,NE.Aliases)
  935. else
  936. Result:=Cardinal(Addr)=Cardinal(NE.Addr);
  937. end;
  938. Close(f);
  939. If Result then
  940. begin
  941. N.Name:=NE.Name;
  942. N.Addr:=nettohost(NE.Addr);
  943. N.Aliases:=NE.Aliases;
  944. end;
  945. end;
  946. end;
  947. end;
  948. Const NoNet : in_addr = (s_addr:0);
  949. Function GetNetworkByName(NetName: String; Var N : TNetworkEntry) : boolean;
  950. begin
  951. Result:=FindNetworkEntryInNetworksFile(NetName,NoNet,N);
  952. end;
  953. Function GetNetworkByAddr(Addr: THostAddr; Var N : TNetworkEntry) : boolean;
  954. begin
  955. Result:=FindNetworkEntryInNetworksFile('',Addr,N);
  956. end;
  957. { ---------------------------------------------------------------------
  958. /etc/services section
  959. ---------------------------------------------------------------------}
  960. Function GetNextServiceEntry(Var F : Text; Var E : TServiceEntry) : Boolean;
  961. Var
  962. Line,S : String;
  963. P : INteger;
  964. begin
  965. Result:=False;
  966. Repeat
  967. ReadLn(F,Line);
  968. StripComment(Line);
  969. S:=NextWord(Line);
  970. If (S<>'') then
  971. begin
  972. E.Name:=S;
  973. S:=NextWord(Line);
  974. P:=Pos('/',S);
  975. If (P<>0) then
  976. begin
  977. E.Port:=StrToIntDef(Copy(S,1,P-1),0);
  978. If (E.Port<>0) then
  979. begin
  980. E.Protocol:=Copy(S,P+1,Length(S)-P);
  981. Result:=length(E.Protocol)>0;
  982. E.Aliases:='';
  983. Repeat
  984. S:=NextWord(Line);
  985. If (S<>'') then
  986. If (Length(E.Aliases)=0) then
  987. E.aliases:=S
  988. else
  989. E.Aliases:=E.Aliases+','+S;
  990. until (S='');
  991. end;
  992. end;
  993. end;
  994. until Result or EOF(F);
  995. end;
  996. Function FindServiceEntryInFile(Const Name,Proto : String; Port : Integer; Var E : TServiceEntry) : Boolean;
  997. Var
  998. F : Text;
  999. TE : TServiceEntry;
  1000. begin
  1001. Result:=False;
  1002. If FileExists(SServicesFile) then
  1003. begin
  1004. Assign(F,SServicesFile);
  1005. {$i-}
  1006. Reset(F);
  1007. {$i+}
  1008. If (IOResult=0) then
  1009. begin
  1010. While Not Result and GetNextServiceEntry(F,TE) do
  1011. begin
  1012. If (Port=-1) then
  1013. Result:=MatchNameOrAlias(Name,TE.Name,TE.Aliases)
  1014. else
  1015. Result:=(Port=TE.Port);
  1016. If Result and (Proto<>'') then
  1017. Result:=(Proto=TE.Protocol);
  1018. end;
  1019. Close(f);
  1020. If Result then
  1021. begin
  1022. E.Name:=TE.Name;
  1023. E.Port:=TE.Port;
  1024. E.Protocol:=TE.Protocol;
  1025. E.Aliases:=TE.Aliases;
  1026. end;
  1027. end;
  1028. end;
  1029. end;
  1030. Function GetServiceByName(Const Name,Proto : String; Var E : TServiceEntry) : Boolean;
  1031. begin
  1032. Result:=FindServiceEntryInFile(Name,Proto,-1,E);
  1033. end;
  1034. Function GetServiceByPort(Port : Word;Const Proto : String; Var E : TServiceEntry) : Boolean;
  1035. begin
  1036. Result:=FindServiceEntryInFile('',Proto,Port,E);
  1037. end;
  1038. { ---------------------------------------------------------------------
  1039. Initialization section
  1040. ---------------------------------------------------------------------}
  1041. Procedure InitResolver;
  1042. Var
  1043. I : Integer;
  1044. begin
  1045. TimeOutS :=5;
  1046. TimeOutMS:=0;
  1047. CheckResolveFileAge:=False;
  1048. If FileExists(SResolveFile) then
  1049. GetDNsservers(SResolveFile);
  1050. end;
  1051. begin
  1052. InitResolver;
  1053. end.