netdb.pp 29 KB

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