netdb.pp 29 KB

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