replace internal uses of gettimeofday with monotonic time where appropriate. make...
[lcore.git] / dnssync.pas
old mode 100755 (executable)
new mode 100644 (file)
index b682acf..84caf9a
@@ -13,7 +13,7 @@ interface
   uses\r
     dnscore,\r
     binipstuff,\r
-    {$ifdef win32}\r
+    {$ifdef mswindows}\r
       winsock,\r
       windows,\r
     {$else}\r
@@ -25,24 +25,26 @@ interface
       sockets,\r
       fd_utils,\r
     {$endif}\r
-    sysutils;\r
+    lcorernd,\r
+    sysutils,\r
+    ltimevalstuff;\r
 \r
 //convert a name to an IP\r
 //will return v4 or v6 depending on what seems favorable, or manual preference setting\r
-//on error the binip will have a family of 0 (other fiels are also currently\r
+//on error the binip will have a family of 0 (other fields are also currently\r
 //zeroed out but may be used for further error information in future)\r
-//timeout is in miliseconds, it is ignored when using windows dns\r
-function forwardlookup(name:string;timeout:integer):tbinip;\r
+//timeout is in milliseconds, it is ignored when using windows dns\r
+function forwardlookup(name:ansistring;timeout:integer):tbinip;\r
 \r
 //convert a name to a list of all IP's returned\r
 //this returns both v4 and v6 IP's, or possibly only v4 or v6, depending on settings\r
 //on error, returns an empty list\r
-function forwardlookuplist(name:string;timeout:integer):tbiniplist;\r
+function forwardlookuplist(name:ansistring;timeout:integer):tbiniplist;\r
 \r
 \r
 //convert an IP to a name, on error a null string will be returned, other\r
 //details as above\r
-function reverselookup(ip:tbinip;timeout:integer):string;\r
+function reverselookup(ip:tbinip;timeout:integer):ansistring;\r
 \r
 \r
 \r
@@ -54,33 +56,27 @@ const
   defaulttimeout=10000;\r
   const mintimeout=16;\r
 \r
-var\r
-  dnssyncserver:string;\r
-  id:integer;\r
+  toport='53';\r
 \r
-  sendquerytime:array[0..numsock-1] of integer;\r
 implementation\r
 \r
-{$ifdef win32}\r
+{$ifdef mswindows}\r
   uses dnswin;\r
 {$endif}\r
 \r
 \r
-{$ifndef win32}\r
+{$ifndef mswindows}\r
 {$define syncdnscore}\r
 {$endif}\r
 \r
 {$i unixstuff.inc}\r
-{$i ltimevalstuff.inc}\r
 \r
-var\r
-  numsockused:integer;\r
-  fd:array[0..numsock-1] of integer;\r
-  state:array[0..numsock-1] of tdnsstate;\r
+type tdnsstatearr=array[0..numsock-1] of tdnsstate;\r
 \r
 {$ifdef syncdnscore}\r
 \r
-{$ifdef win32}\r
+\r
+{$ifdef mswindows}\r
   const\r
     winsocket = 'wsock32.dll';\r
   function sendto(s: TSocket; const Buf; len, flags: Integer; var addrto: TinetSockAddrV; tolen: Integer): Integer; stdcall; external    winsocket name 'sendto';\r
@@ -91,64 +87,78 @@ var
 \r
 \r
 function getts:integer;\r
-{$ifdef win32}\r
+{$ifdef mswindows}\r
 begin\r
   result := GetTickCount and tsmask;\r
 {$else}\r
 var\r
   temp:ttimeval;\r
 begin\r
-  gettimeofday(temp);\r
+  gettimemonotonic(temp);\r
   result := ((temp.tv_usec div 1000) + (temp.tv_sec * 1000)) and tsmask;\r
 {$endif}\r
 end;\r
 \r
-\r
-function sendquery(socknum:integer;const packet:tdnspacket;len:integer):boolean;\r
+procedure resolveloop(timeout:integer;var state:tdnsstatearr;numsockused:integer);\r
 var\r
-  a:integer;\r
-  addr       : string;\r
-  port       : string;\r
-  inaddr     : TInetSockAddrV;\r
-begin\r
-{  writeln('sendquery ',decodename(state.packet,state.packetlen,12,0,a),' ',state.requesttype);}\r
-  result := false;\r
-  if len = 0 then exit; {no packet}\r
+  selectresult   : integer;\r
+  fds            : fdset;\r
+\r
+  endtime      : longint;\r
+  starttime    : longint;\r
+  wrapmode     : boolean;\r
+  currenttime  : integer;\r
 \r
-  if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);\r
-  port := '53';\r
+  lag            : ttimeval;\r
+  selecttimeout         : ttimeval;\r
+  socknum:integer;\r
+  needprocessing:array[0..numsock-1] of boolean;\r
+  finished:array[0..numsock-1] of boolean;\r
+  a,b:integer;\r
 \r
-  makeinaddrv(ipstrtobinf(addr),port,inaddr);\r
+  Src    : TInetSockAddrV;\r
+  Srcx   : {$ifdef mswindows}sockaddr_in{$else}TInetSockAddrV{$endif} absolute Src;\r
+  SrcLen : Integer;\r
+  fromip:tbinip;\r
+  fromport:ansistring;\r
+\r
+  fd:array[0..numsock-1] of integer;\r
+  toaddr:array[0..numsock-1] of tbinip;\r
+  id:integer;\r
+  sendquerytime:array[0..numsock-1] of integer;\r
 \r
-  sendto(fd[socknum],packet,len,0,inaddr,inaddrsize(inaddr));\r
-  sendquerytime[socknum] := getts;\r
-  result := true;\r
-end;\r
 \r
 procedure setupsocket;\r
 var\r
   inAddrtemp : TInetSockAddrV;\r
-  a:integer;\r
   biniptemp:tbinip;\r
-  addr:string;\r
+  a,retrycount,porttemp:integer;\r
+  bindresult:boolean;\r
 begin\r
-  //init both sockets smultaneously, always, so they get succesive fd's\r
-  if fd[0] > 0 then exit;\r
-\r
-  if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);\r
+  biniptemp := getcurrentsystemnameserverbin(id);\r
   //must get the DNS server here so we know to init v4 or v6\r
 \r
-  fillchar(inaddrtemp,sizeof(inaddrtemp),0);\r
-  ipstrtobin(addr,biniptemp);\r
-  if biniptemp.family = 0 then biniptemp.family := AF_INET;\r
+  if biniptemp.family = AF_INET6 then biniptemp := ipstrtobinf('::') else biniptemp := ipstrtobinf('0.0.0.0');\r
 \r
-  inaddrtemp.inaddr.family := biniptemp.family;\r
 \r
   for a := 0 to numsockused-1 do begin\r
-    fd[a] := Socket(biniptemp.family,SOCK_DGRAM,0);\r
+    retrycount := 5;\r
+    repeat\r
+      if (retrycount <= 1) then begin\r
+        porttemp := 0; //for the last attempt let the OS decide\r
+      end else begin\r
+        porttemp := 1024 + randominteger(65536 - 1024);\r
+      end;\r
+\r
+      makeinaddrv(biniptemp,inttostr( porttemp ),inaddrtemp);\r
+\r
+      fd[a] := Socket(biniptemp.family,SOCK_DGRAM,0);\r
+      bindresult := {$ifdef mswindows}Not{$endif} Bind(fd[a],inAddrtemp,inaddrsize(inaddrtemp));\r
+      dec(retrycount);\r
+    until (retrycount <= 0) or (bindresult);\r
 \r
-    If {$ifndef win32}Not{$endif} Bind(fd[a],inAddrtemp,inaddrsize(inaddrtemp)) Then begin\r
-      {$ifdef win32}\r
+    If (not bindresult) Then begin\r
+      {$ifdef mswindows}\r
         raise Exception.create('unable to bind '+inttostr(WSAGetLastError));\r
       {$else}\r
         raise Exception.create('unable to bind '+inttostr(socketError));\r
@@ -157,37 +167,53 @@ begin
   end;\r
 end;\r
 \r
-procedure resolveloop(timeout:integer);\r
+procedure cleanupsockets;\r
 var\r
-  selectresult   : integer;\r
-  fds            : fdset;\r
+  a:integer;\r
+begin\r
+  for a := 0 to numsockused-1 do closesocket(fd[a]);\r
+end;\r
 \r
-  endtime      : longint;\r
-  starttime    : longint;\r
-  wrapmode     : boolean;\r
-  currenttime  : integer;\r
+function sendquery(socknum:integer;const packet:tdnspacket;len:integer):boolean;\r
+var\r
+  ip       : tbinip;\r
+  port       : ansistring;\r
+  inaddr     : TInetSockAddrV;\r
+begin\r
+{  writeln('sendquery ',decodename(state.packet,state.packetlen,12,0,a),' ',state.requesttype);}\r
+  result := false;\r
+  if len = 0 then exit; {no packet}\r
 \r
-  lag            : ttimeval;\r
-  currenttimeout : ttimeval;\r
-  selecttimeout         : ttimeval;\r
-  socknum:integer;\r
-  needprocessing:array[0..numsock-1] of boolean;\r
-  finished:array[0..numsock-1] of boolean;\r
-  a,b:integer;\r
+  ip := getcurrentsystemnameserverbin(id);\r
+\r
+  {$ifdef ipv6}{$ifdef mswindows}\r
+  if toaddr[socknum].family = AF_INET6 then if (useaf = 0) then useaf := useaf_preferv6;\r
+  {$endif}{$endif}\r
+\r
+  port := toport;\r
+  toaddr[socknum] := ip;\r
+  makeinaddrv(toaddr[socknum],port,inaddr);\r
+\r
+  sendto(fd[socknum],packet,len,0,inaddr,inaddrsize(inaddr));\r
+  sendquerytime[socknum] := getts;\r
+  result := true;\r
+end;\r
 \r
 begin\r
   if timeout < mintimeout then timeout := defaulttimeout;\r
 \r
-    starttime := getts;\r
-    endtime := starttime + timeout;\r
-    if (endtime and tswrap)=0 then begin\r
-      wrapmode := false;\r
-    end else begin\r
-      wrapmode := true;\r
-    end;\r
-    endtime := endtime and tsmask;\r
+  starttime := getts;\r
+  endtime := starttime + timeout;\r
+  if (endtime and tswrap)=0 then begin\r
+    wrapmode := false;\r
+  end else begin\r
+    wrapmode := true;\r
+  end;\r
+  endtime := endtime and tsmask;\r
 \r
   setupsocket;\r
+\r
+\r
   for socknum := 0 to numsockused-1 do begin\r
     needprocessing[socknum] := true;\r
     finished[socknum] := false;\r
@@ -208,6 +234,7 @@ begin
             if finished[a] then inc(b);\r
           end;\r
           if (b = numsockused) then begin\r
+            cleanupsockets;\r
             exit;\r
           end;\r
           //onrequestdone(self,0);\r
@@ -229,7 +256,7 @@ begin
       selecttimeout.tv_sec := 0;\r
       selecttimeout.tv_usec := retryafter;\r
     end;\r
-    //find the highest of the used fd's\r
+    //find the highest of the used fds\r
     b := 0;\r
     for socknum := numsockused-1 downto 0 do if fd[socknum] > b then b := fd[socknum];\r
     selectresult := select(b+1,@fds,nil,nil,@selecttimeout);\r
@@ -242,10 +269,22 @@ begin
         fillchar(state[socknum].recvpacket,sizeof(state[socknum].recvpacket),0);\r
         msectotimeval(lag,(currenttime-sendquerytime[socknum]) and tsmask);\r
 \r
-        if dnssyncserver = '' then reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);\r
-        state[socknum].recvpacketlen := recv(fd[socknum],state[socknum].recvpacket, SizeOf(state[socknum].recvpacket),0);\r
-        state[socknum].parsepacket := true;\r
-        needprocessing[socknum] := true;\r
+        reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);\r
+\r
+        SrcLen := SizeOf(Src);\r
+        state[socknum].recvpacketlen := recvfrom(fd[socknum],state[socknum].recvpacket, SizeOf(state[socknum].recvpacket),0,Srcx,SrcLen);\r
+\r
+        if (state[socknum].recvpacketlen > 0) then begin\r
+          fromip := inaddrvtobinip(Src);\r
+          fromport := inttostr(htons(src.InAddr.port));\r
+          if ((not comparebinip(toaddr[socknum],fromip)) or (fromport <> toport)) then begin\r
+//            writeln('dnssync received from wrong IP:port ',ipbintostr(fromip),'#',fromport);\r
+            state[socknum].recvpacketlen := 0;\r
+          end else begin\r
+            state[socknum].parsepacket := true;\r
+            needprocessing[socknum] := true;\r
+          end;\r
+        end;\r
       end;\r
     end;\r
     if selectresult < 0 then exit;\r
@@ -253,8 +292,9 @@ begin
 \r
       currenttime := getts;\r
 \r
-      if dnssyncserver = '' then reportlag(id,-1);\r
+      reportlag(id,-1);\r
       if (currenttime >= endtime) and ((not wrapmode) or (currenttime < starttime)) then begin\r
+        cleanupsockets;\r
         exit;\r
       end else begin\r
         //resend\r
@@ -269,12 +309,16 @@ end;
 \r
 \r
 \r
-function forwardlookuplist(name:string;timeout:integer):tbiniplist;\r
+function forwardlookuplist(name:ansistring;timeout:integer):tbiniplist;\r
 var\r
   dummy : integer;\r
-  a,b:integer;\r
+  a:integer;\r
   biniptemp:tbinip;\r
   l:tbiniplist;\r
+\r
+  numsockused:integer;\r
+  state:tdnsstatearr;\r
+\r
 begin\r
   ipstrtobin(name,biniptemp);\r
   if biniptemp.family <> 0 then begin\r
@@ -283,7 +327,7 @@ begin
     exit; //it was an IP address, no need for dns\r
   end;\r
 \r
-  {$ifdef win32}\r
+  {$ifdef mswindows}\r
   if usewindns then begin\r
     if (useaf = useaf_v4) then a := af_inet else if (useaf = useaf_v6) then a := af_inet6 else a := 0;\r
     result := winforwardlookuplist(name,a,dummy);\r
@@ -307,7 +351,7 @@ begin
   {$endif}\r
   begin\r
   {$ifdef syncdnscore}\r
-    {$ifdef linux}{$ifdef ipv6}initpreferredmode;{$endif}{$endif}\r
+    {$ifdef ipv6}initpreferredmode;{$endif}\r
 \r
     numsockused := 0;\r
 \r
@@ -323,7 +367,7 @@ begin
     end;\r
     {$endif}\r
 \r
-    resolveloop(timeout);\r
+    resolveloop(timeout,state,numsockused);\r
 \r
     if (numsockused = 1) then begin\r
       biniplist_addlist(result,state[0].resultlist);\r
@@ -334,13 +378,13 @@ begin
     end else begin\r
       biniplist_addlist(result,state[0].resultlist);\r
       biniplist_addlist(result,state[1].resultlist);\r
-    {$endif}  \r
+    {$endif}\r
     end;\r
     {$endif}\r
   end;\r
 end;\r
 \r
-function forwardlookup(name:string;timeout:integer):tbinip;\r
+function forwardlookup(name:ansistring;timeout:integer):tbinip;\r
 var\r
   listtemp:tbiniplist;\r
 begin\r
@@ -348,11 +392,13 @@ begin
   result := biniplist_get(listtemp,0);\r
 end;\r
 \r
-function reverselookup(ip:tbinip;timeout:integer):string;\r
+function reverselookup(ip:tbinip;timeout:integer):ansistring;\r
 var\r
   dummy : integer;\r
+  numsockused:integer;\r
+  state:tdnsstatearr;\r
 begin\r
-  {$ifdef win32}\r
+  {$ifdef mswindows}\r
     if usewindns then begin\r
       result := winreverselookup(ip,dummy);\r
       exit;\r
@@ -361,12 +407,12 @@ begin
   {$ifdef syncdnscore}\r
   setstate_reverse(ip,state[0]);\r
   numsockused := 1;\r
-  resolveloop(timeout);\r
+  resolveloop(timeout,state,numsockused);\r
   result := state[0].resultstr;\r
   {$endif}\r
 end;\r
 \r
-{$ifdef win32}\r
+{$ifdef mswindows}\r
   var\r
     wsadata : twsadata;\r
 \r