Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

dnssync.pas 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. { Copyright (C) 2005 Bas Steendijk and Peter Green
  2. For conditions of distribution and use, see copyright notice in zlib_license.txt
  3. which is included in the package
  4. ----------------------------------------------------------------------------- }
  5. unit dnssync;
  6. {$ifdef fpc}
  7. {$mode delphi}
  8. {$endif}
  9. {$include lcoreconfig.inc}
  10. interface
  11. uses
  12. dnscore,
  13. binipstuff,
  14. {$ifdef win32}
  15. winsock,
  16. windows,
  17. {$else}
  18. {$ifdef VER1_0}
  19. linux,
  20. {$else}
  21. baseunix,unix,unixutil,
  22. {$endif}
  23. sockets,
  24. fd_utils,
  25. {$endif}
  26. lcorernd,
  27. sysutils;
  28. //convert a name to an IP
  29. //will return v4 or v6 depending on what seems favorable, or manual preference setting
  30. //on error the binip will have a family of 0 (other fiels are also currently
  31. //zeroed out but may be used for further error information in future)
  32. //timeout is in miliseconds, it is ignored when using windows dns
  33. function forwardlookup(name:string;timeout:integer):tbinip;
  34. //convert a name to a list of all IP's returned
  35. //this returns both v4 and v6 IP's, or possibly only v4 or v6, depending on settings
  36. //on error, returns an empty list
  37. function forwardlookuplist(name:string;timeout:integer):tbiniplist;
  38. //convert an IP to a name, on error a null string will be returned, other
  39. //details as above
  40. function reverselookup(ip:tbinip;timeout:integer):string;
  41. const
  42. tswrap=$4000;
  43. tsmask=tswrap-1;
  44. numsock=1{$ifdef ipv6}+1{$endif};
  45. defaulttimeout=10000;
  46. const mintimeout=16;
  47. toport='53';
  48. var
  49. id:integer;
  50. sendquerytime:array[0..numsock-1] of integer;
  51. implementation
  52. {$ifdef win32}
  53. uses dnswin;
  54. {$endif}
  55. {$ifndef win32}
  56. {$define syncdnscore}
  57. {$endif}
  58. {$i unixstuff.inc}
  59. {$i ltimevalstuff.inc}
  60. var
  61. numsockused:integer;
  62. fd:array[0..numsock-1] of integer;
  63. state:array[0..numsock-1] of tdnsstate;
  64. toaddr:array[0..numsock-1] of tbinip;
  65. {$ifdef syncdnscore}
  66. {$ifdef win32}
  67. const
  68. winsocket = 'wsock32.dll';
  69. function sendto(s: TSocket; const Buf; len, flags: Integer; var addrto: TinetSockAddrV; tolen: Integer): Integer; stdcall; external winsocket name 'sendto';
  70. function bind(s: TSocket; var addr: TinetSockAddrV; namelen: Integer): Longbool; stdcall; external winsocket name 'bind';
  71. type
  72. fdset=tfdset;
  73. {$endif}
  74. function getts:integer;
  75. {$ifdef win32}
  76. begin
  77. result := GetTickCount and tsmask;
  78. {$else}
  79. var
  80. temp:ttimeval;
  81. begin
  82. gettimeofday(temp);
  83. result := ((temp.tv_usec div 1000) + (temp.tv_sec * 1000)) and tsmask;
  84. {$endif}
  85. end;
  86. function sendquery(socknum:integer;const packet:tdnspacket;len:integer):boolean;
  87. var
  88. a:integer;
  89. addr : string;
  90. port : string;
  91. inaddr : TInetSockAddrV;
  92. begin
  93. { writeln('sendquery ',decodename(state.packet,state.packetlen,12,0,a),' ',state.requesttype);}
  94. result := false;
  95. if len = 0 then exit; {no packet}
  96. if overridednsserver <> '' then addr := overridednsserver else addr := getcurrentsystemnameserver(id);
  97. {$ifdef ipv6}{$ifdef win32}
  98. if toaddr[socknum].family = AF_INET6 then if (useaf = 0) then useaf := useaf_preferv6;
  99. {$endif}{$endif}
  100. port := toport;
  101. toaddr[socknum] := ipstrtobinf(addr);
  102. makeinaddrv(toaddr[socknum],port,inaddr);
  103. sendto(fd[socknum],packet,len,0,inaddr,inaddrsize(inaddr));
  104. sendquerytime[socknum] := getts;
  105. result := true;
  106. end;
  107. procedure setupsocket;
  108. var
  109. inAddrtemp : TInetSockAddrV;
  110. a:integer;
  111. biniptemp:tbinip;
  112. addr:string;
  113. begin
  114. //init both sockets smultaneously, always, so they get succesive fd's
  115. if fd[0] > 0 then exit;
  116. if overridednsserver <> '' then addr := overridednsserver else addr := getcurrentsystemnameserver(id);
  117. //must get the DNS server here so we know to init v4 or v6
  118. ipstrtobin(addr,biniptemp);
  119. if biniptemp.family = AF_INET6 then biniptemp := ipstrtobinf('::') else biniptemp := ipstrtobinf('0.0.0.0');
  120. for a := 0 to numsockused-1 do begin
  121. makeinaddrv(biniptemp,inttostr( 1024 + randominteger(65536 - 1024) ),inaddrtemp);
  122. fd[a] := Socket(biniptemp.family,SOCK_DGRAM,0);
  123. If {$ifndef win32}Not{$endif} Bind(fd[a],inAddrtemp,inaddrsize(inaddrtemp)) Then begin
  124. {$ifdef win32}
  125. raise Exception.create('unable to bind '+inttostr(WSAGetLastError));
  126. {$else}
  127. raise Exception.create('unable to bind '+inttostr(socketError));
  128. {$endif}
  129. end;
  130. end;
  131. end;
  132. procedure resolveloop(timeout:integer);
  133. var
  134. selectresult : integer;
  135. fds : fdset;
  136. endtime : longint;
  137. starttime : longint;
  138. wrapmode : boolean;
  139. currenttime : integer;
  140. lag : ttimeval;
  141. currenttimeout : ttimeval;
  142. selecttimeout : ttimeval;
  143. socknum:integer;
  144. needprocessing:array[0..numsock-1] of boolean;
  145. finished:array[0..numsock-1] of boolean;
  146. a,b:integer;
  147. Src : TInetSockAddrV;
  148. Srcx : {$ifdef win32}sockaddr_in{$else}TInetSockAddrV{$endif} absolute Src;
  149. SrcLen : Integer;
  150. fromip:tbinip;
  151. fromport:string;
  152. begin
  153. if timeout < mintimeout then timeout := defaulttimeout;
  154. starttime := getts;
  155. endtime := starttime + timeout;
  156. if (endtime and tswrap)=0 then begin
  157. wrapmode := false;
  158. end else begin
  159. wrapmode := true;
  160. end;
  161. endtime := endtime and tsmask;
  162. setupsocket;
  163. for socknum := 0 to numsockused-1 do begin
  164. needprocessing[socknum] := true;
  165. finished[socknum] := false;
  166. end;
  167. repeat
  168. for socknum := numsockused-1 downto 0 do if needprocessing[socknum] then begin
  169. state_process(state[socknum]);
  170. case state[socknum].resultaction of
  171. action_ignore: begin
  172. {do nothing}
  173. end;
  174. action_done: begin
  175. finished[socknum] := true;
  176. //exit if all resolvers are finished
  177. b := 0;
  178. for a := 0 to numsockused-1 do begin
  179. if finished[a] then inc(b);
  180. end;
  181. if (b = numsockused) then begin
  182. exit;
  183. end;
  184. //onrequestdone(self,0);
  185. end;
  186. action_sendquery:begin
  187. { writeln('send query');}
  188. sendquery(socknum,state[socknum].sendpacket,state[socknum].sendpacketlen);
  189. end;
  190. end;
  191. needprocessing[socknum] := false;
  192. end;
  193. currenttime := getts;
  194. msectotimeval(selecttimeout, (endtime-currenttime) and tsmask);
  195. fd_zero(fds);
  196. for socknum := numsockused-1 downto 0 do if not finished[socknum] then fd_set(fd[socknum],fds);
  197. if (selecttimeout.tv_sec > 0) or (selecttimeout.tv_usec > retryafter) then begin
  198. selecttimeout.tv_sec := 0;
  199. selecttimeout.tv_usec := retryafter;
  200. end;
  201. //find the highest of the used fd's
  202. b := 0;
  203. for socknum := numsockused-1 downto 0 do if fd[socknum] > b then b := fd[socknum];
  204. selectresult := select(b+1,@fds,nil,nil,@selecttimeout);
  205. if selectresult > 0 then begin
  206. currenttime := getts;
  207. for socknum := numsockused-1 downto 0 do if fd_isset(fd[socknum],fds) then begin
  208. { writeln('selectresult>0');}
  209. //why the hell are we zeroing out the packet buffer before reading into it?! --plugwash
  210. fillchar(state[socknum].recvpacket,sizeof(state[socknum].recvpacket),0);
  211. msectotimeval(lag,(currenttime-sendquerytime[socknum]) and tsmask);
  212. if overridednsserver = '' then reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);
  213. SrcLen := SizeOf(Src);
  214. state[socknum].recvpacketlen := recvfrom(fd[socknum],state[socknum].recvpacket, SizeOf(state[socknum].recvpacket),0,Srcx,SrcLen);
  215. if (state[socknum].recvpacketlen > 0) then begin
  216. fromip := inaddrvtobinip(Src);
  217. fromport := inttostr(htons(src.InAddr.port));
  218. if ((not comparebinip(toaddr[socknum],fromip)) or (fromport <> toport)) then begin
  219. // writeln('dnssync received from wrong IP:port ',ipbintostr(fromip),'#',fromport);
  220. state[socknum].recvpacketlen := 0;
  221. end else begin
  222. state[socknum].parsepacket := true;
  223. needprocessing[socknum] := true;
  224. end;
  225. end;
  226. end;
  227. end;
  228. if selectresult < 0 then exit;
  229. if selectresult = 0 then begin
  230. currenttime := getts;
  231. if overridednsserver = '' then reportlag(id,-1);
  232. if (currenttime >= endtime) and ((not wrapmode) or (currenttime < starttime)) then begin
  233. exit;
  234. end else begin
  235. //resend
  236. for socknum := numsockused-1 downto 0 do begin
  237. sendquery(socknum,state[socknum].sendpacket,state[socknum].sendpacketlen);
  238. end;
  239. end;
  240. end;
  241. until false;
  242. end;
  243. {$endif}
  244. function forwardlookuplist(name:string;timeout:integer):tbiniplist;
  245. var
  246. dummy : integer;
  247. a,b:integer;
  248. biniptemp:tbinip;
  249. l:tbiniplist;
  250. begin
  251. ipstrtobin(name,biniptemp);
  252. if biniptemp.family <> 0 then begin
  253. result := biniplist_new;
  254. biniplist_add(result,biniptemp);
  255. exit; //it was an IP address, no need for dns
  256. end;
  257. {$ifdef win32}
  258. if usewindns then begin
  259. if (useaf = useaf_v4) then a := af_inet else if (useaf = useaf_v6) then a := af_inet6 else a := 0;
  260. result := winforwardlookuplist(name,a,dummy);
  261. {$ifdef ipv6}
  262. if (useaf = useaf_preferv4) then begin
  263. {prefer mode: sort the IP's}
  264. l := biniplist_new;
  265. addipsoffamily(l,result,af_inet);
  266. addipsoffamily(l,result,af_inet6);
  267. result := l;
  268. end;
  269. if (useaf = useaf_preferv6) then begin
  270. {prefer mode: sort the IP's}
  271. l := biniplist_new;
  272. addipsoffamily(l,result,af_inet6);
  273. addipsoffamily(l,result,af_inet);
  274. result := l;
  275. end;
  276. {$endif}
  277. end else
  278. {$endif}
  279. begin
  280. {$ifdef syncdnscore}
  281. {$ifdef ipv6}initpreferredmode;{$endif}
  282. numsockused := 0;
  283. result := biniplist_new;
  284. if (useaf <> useaf_v6) then begin
  285. setstate_forward(name,state[numsockused],af_inet);
  286. inc(numsockused);
  287. end;
  288. {$ifdef ipv6}
  289. if (useaf <> useaf_v4) then begin
  290. setstate_forward(name,state[numsockused],af_inet6);
  291. inc(numsockused);
  292. end;
  293. {$endif}
  294. resolveloop(timeout);
  295. if (numsockused = 1) then begin
  296. biniplist_addlist(result,state[0].resultlist);
  297. {$ifdef ipv6}
  298. end else if (useaf = useaf_preferv6) then begin
  299. biniplist_addlist(result,state[1].resultlist);
  300. biniplist_addlist(result,state[0].resultlist);
  301. end else begin
  302. biniplist_addlist(result,state[0].resultlist);
  303. biniplist_addlist(result,state[1].resultlist);
  304. {$endif}
  305. end;
  306. {$endif}
  307. end;
  308. end;
  309. function forwardlookup(name:string;timeout:integer):tbinip;
  310. var
  311. listtemp:tbiniplist;
  312. begin
  313. listtemp := forwardlookuplist(name,timeout);
  314. result := biniplist_get(listtemp,0);
  315. end;
  316. function reverselookup(ip:tbinip;timeout:integer):string;
  317. var
  318. dummy : integer;
  319. begin
  320. {$ifdef win32}
  321. if usewindns then begin
  322. result := winreverselookup(ip,dummy);
  323. exit;
  324. end;
  325. {$endif}
  326. {$ifdef syncdnscore}
  327. setstate_reverse(ip,state[0]);
  328. numsockused := 1;
  329. resolveloop(timeout);
  330. result := state[0].resultstr;
  331. {$endif}
  332. end;
  333. {$ifdef win32}
  334. var
  335. wsadata : twsadata;
  336. initialization
  337. WSAStartUp($2,wsadata);
  338. finalization
  339. WSACleanUp;
  340. {$endif}
  341. end.