X-Git-Url: http://www.lcore.org/git/lcore.git/blobdiff_plain/4782a5c5afee47721cc617daa40dd29828342c2b..8dbf997db86622966c80c12998c2b39577de949f:/dnssync.pas

diff --git a/dnssync.pas b/dnssync.pas
index 379aa05..3632b29 100755
--- a/dnssync.pas
+++ b/dnssync.pas
@@ -7,6 +7,8 @@ unit dnssync;
   {$mode delphi}
 {$endif}
 
+{$include lcoreconfig.inc}
+
 interface
   uses
     dnscore,
@@ -26,54 +28,95 @@ interface
     sysutils;
 
 //convert a name to an IP
-//IPV4 is preffered if availible, if not IPV6 will be returned (if ipv6 support
-//compiled in)
+//will return v4 or v6 depending on what seems favorable, or manual preference setting
 //on error the binip will have a family of 0 (other fiels are also currently
 //zeroed out but may be used for further error information in future)
-//timeout is in seconds, it is ignored when using windows dns
+//timeout is in miliseconds, it is ignored when using windows dns
 function forwardlookup(name:string;timeout:integer):tbinip;
 
+//convert a name to a list of all IP's returned
+//this returns both v4 and v6 IP's, or possibly only v4 or v6, depending on settings
+//on error, returns an empty list
+function forwardlookuplist(name:string;timeout:integer):tbiniplist;
+
 
-//convert an IP to a name, on error a null string will be returned, other 
+//convert an IP to a name, on error a null string will be returned, other
 //details as above
 function reverselookup(ip:tbinip;timeout:integer):string;
 
+{$ifdef linux}{$ifdef ipv6}
+function getv6localips:tbiniplist;
+procedure initpreferredmode;
+
+var
+  preferredmodeinited:boolean;
+
+{$endif}{$endif}
+
+const
+  tswrap=$4000;
+  tsmask=tswrap-1;
+
+  numsock=1{$ifdef ipv6}+1{$endif};
+  defaulttimeout=10000;
+  const mintimeout=16;
 
 var
   dnssyncserver:string;
-  id : integer;
-  {$ifdef win32}
-    sendquerytime : integer;
-  {$else}
-    sendquerytime : ttimeval;
-  {$endif}
+  id:integer;
+
+  sendquerytime:array[0..numsock-1] of integer;
 implementation
+
 {$ifdef win32}
   uses dnswin;
 {$endif}
 
+
+{$ifndef win32}
+{$define syncdnscore}
+{$endif}
+
 {$i unixstuff.inc}
 {$i ltimevalstuff.inc}
 
 var
-  fd:integer;
-  state:tdnsstate;
+  numsockused:integer;
+  fd:array[0..numsock-1] of integer;
+  state:array[0..numsock-1] of tdnsstate;
+
+{$ifdef syncdnscore}
+
 {$ifdef win32}
   const
     winsocket = 'wsock32.dll';
-  function sendto(s: TSocket; const Buf; len, flags: Integer; var addrto: TinetSockAddr; tolen: Integer): Integer; stdcall; external    winsocket name 'sendto';
-  function bind(s: TSocket; var addr: TinetSockAddr; namelen: Integer): Longbool; stdcall; external    winsocket name 'bind';
+  function sendto(s: TSocket; const Buf; len, flags: Integer; var addrto: TinetSockAddrV; tolen: Integer): Integer; stdcall; external    winsocket name 'sendto';
+  function bind(s: TSocket; var addr: TinetSockAddrV; namelen: Integer): Longbool; stdcall; external    winsocket name 'bind';
   type
     fdset=tfdset;
 {$endif}
 
-function sendquery(const packet:tdnspacket;len:integer):boolean;
+
+function getts:integer;
+{$ifdef win32}
+begin
+  result := GetTickCount and tsmask;
+{$else}
+var
+  temp:ttimeval;
+begin
+  gettimeofday(temp);
+  result := ((temp.tv_usec div 1000) + (temp.tv_sec * 1000)) and tsmask;
+{$endif}
+end;
+
+
+function sendquery(socknum:integer;const packet:tdnspacket;len:integer):boolean;
 var
   a:integer;
   addr       : string;
   port       : string;
-  inaddr     : TInetSockAddr;
-
+  inaddr     : TInetSockAddrV;
 begin
 {  writeln('sendquery ',decodename(state.packet,state.packetlen,12,0,a),' ',state.requesttype);}
   result := false;
@@ -82,35 +125,42 @@ begin
   if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);
   port := '53';
 
-  inAddr.family:=AF_INET;
-  inAddr.port:=htons(strtointdef(port,0));
-  inAddr.addr:=htonl(longip(addr));
+  makeinaddrv(ipstrtobinf(addr),port,inaddr);
 
-  sendto(fd,packet,len,0,inaddr,sizeof(inaddr));
-  {$ifdef win32}
-    sendquerytime := GetTickCount and $3fff;
-  {$else}
-    gettimeofday(sendquerytime);
-  {$endif}
+  sendto(fd[socknum],packet,len,0,inaddr,inaddrsize(inaddr));
+  sendquerytime[socknum] := getts;
   result := true;
 end;
 
 procedure setupsocket;
 var
-  inAddrtemp : TInetSockAddr;
+  inAddrtemp : TInetSockAddrV;
+  a:integer;
+  biniptemp:tbinip;
+  addr:string;
 begin
-  if fd > 0 then exit;
+  //init both sockets smultaneously, always, so they get succesive fd's
+  if fd[0] > 0 then exit;
 
-  fd := Socket(AF_INET,SOCK_DGRAM,0);
-  inAddrtemp.family:=AF_INET;
-  inAddrtemp.port:=0;
-  inAddrtemp.addr:=0;{htonl(longip('0.0.0.0'));}
-  If {$ifndef win32}Not{$endif} Bind(fd,inAddrtemp,SizeOf(inAddrtemp)) Then begin
-    {$ifdef win32}
-      raise Exception.create('unable to bind '+inttostr(WSAGetLastError));
-    {$else}
-      raise Exception.create('unable to bind '+inttostr(socketError));
-    {$endif}
+  if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);
+  //must get the DNS server here so we know to init v4 or v6
+
+  fillchar(inaddrtemp,sizeof(inaddrtemp),0);
+  ipstrtobin(addr,biniptemp);
+  if biniptemp.family = 0 then biniptemp.family := AF_INET;
+
+  inaddrtemp.inaddr.family := biniptemp.family;
+
+  for a := 0 to numsockused-1 do begin
+    fd[a] := Socket(biniptemp.family,SOCK_DGRAM,0);
+
+    If {$ifndef win32}Not{$endif} Bind(fd[a],inAddrtemp,inaddrsize(inaddrtemp)) Then begin
+      {$ifdef win32}
+        raise Exception.create('unable to bind '+inttostr(WSAGetLastError));
+      {$else}
+        raise Exception.create('unable to bind '+inttostr(socketError));
+      {$endif}
+    end;
   end;
 end;
 
@@ -118,119 +168,201 @@ procedure resolveloop(timeout:integer);
 var
   selectresult   : integer;
   fds            : fdset;
-  {$ifdef win32}
-    endtime      : longint;
-    starttime    : longint;
-    wrapmode     : boolean;
-    currenttime  : integer;
-  {$else}
-    endtime      : ttimeval;
-    currenttime    : ttimeval;
 
-  {$endif}
+  endtime      : longint;
+  starttime    : longint;
+  wrapmode     : boolean;
+  currenttime  : integer;
+
   lag            : ttimeval;
   currenttimeout : ttimeval;
   selecttimeout	 : ttimeval;
-
+  socknum:integer;
+  needprocessing:array[0..numsock-1] of boolean;
+  finished:array[0..numsock-1] of boolean;
+  a,b:integer;
 
 begin
-  {$ifdef win32}
-    starttime := GetTickCount and $3fff;
-    endtime := starttime +(timeout*1000);
-    if (endtime and $4000)=0 then begin
+  if timeout < mintimeout then timeout := defaulttimeout;
+
+    starttime := getts;
+    endtime := starttime + timeout;
+    if (endtime and tswrap)=0 then begin
       wrapmode := false;
     end else begin
       wrapmode := true;
     end;
-    endtime := endtime and $3fff;
-  {$else}
-    gettimeofday(endtime);
-    endtime.tv_sec := endtime.tv_sec + timeout;
-  {$endif}
+    endtime := endtime and tsmask;
 
   setupsocket;
+  for socknum := 0 to numsockused-1 do begin
+    needprocessing[socknum] := true;
+    finished[socknum] := false;
+  end;
+
   repeat
-    state_process(state);
-    case state.resultaction of
-      action_ignore: begin
-{        writeln('ignore');}
-        {do nothing}
-      end;
-      action_done: begin
-{        writeln('done');}
-        exit;
-        //onrequestdone(self,0);
-      end;
-      action_sendquery:begin
+    for socknum := numsockused-1 downto 0 do if needprocessing[socknum] then begin
+      state_process(state[socknum]);
+      case state[socknum].resultaction of
+        action_ignore: begin
+          {do nothing}
+        end;
+        action_done: begin
+          finished[socknum] := true;
+          //exit if all resolvers are finished
+          b := 0;
+          for a := 0 to numsockused-1 do begin
+            if finished[a] then inc(b);
+          end;
+          if (b = numsockused) then begin
+            exit;
+          end;
+          //onrequestdone(self,0);
+        end;
+        action_sendquery:begin
 {        writeln('send query');}
-        sendquery(state.sendpacket,state.sendpacketlen);
+          sendquery(socknum,state[socknum].sendpacket,state[socknum].sendpacketlen);
+        end;
       end;
+      needprocessing[socknum] := false;
     end;
-    {$ifdef win32}
-      currenttime := GetTickCount and $3fff;
-      msectotimeval(selecttimeout, (endtime-currenttime)and$3fff);
-    {$else}
-      gettimeofday(currenttime);
-      selecttimeout := endtime;
-      tv_substract(selecttimeout,currenttime);
-    {$endif}
+
+    currenttime := getts;
+    msectotimeval(selecttimeout, (endtime-currenttime) and tsmask);
+
     fd_zero(fds);
-    fd_set(fd,fds);
+    for socknum := numsockused-1 downto 0 do if not finished[socknum] then fd_set(fd[socknum],fds);
     if (selecttimeout.tv_sec > 0) or (selecttimeout.tv_usec > retryafter) then begin
       selecttimeout.tv_sec := 0;
       selecttimeout.tv_usec := retryafter;
     end;
-    selectresult := select(fd+1,@fds,nil,nil,@selecttimeout);
+    //find the highest of the used fd's
+    b := 0;
+    for socknum := numsockused-1 downto 0 do if fd[socknum] > b then b := fd[socknum];
+    selectresult := select(b+1,@fds,nil,nil,@selecttimeout);
     if selectresult > 0 then begin
-{      writeln('selectresult>0');}
-      //why the hell are we zeroing out the packet buffer before reading into it?! --plugwash
-      fillchar(state.recvpacket,sizeof(state.recvpacket),0);
-      {$ifdef win32}
-        msectotimeval(lag,(currenttime-sendquerytime)and$3fff);
-      {$else}
-        lag := currenttime;
-        tv_substract(lag,sendquerytime);
+      currenttime := getts;
+      for socknum := numsockused-1 downto 0 do if fd_isset(fd[socknum],fds) then begin
+  {      writeln('selectresult>0');}
+        //why the hell are we zeroing out the packet buffer before reading into it?! --plugwash
 
-      {$endif}
+        fillchar(state[socknum].recvpacket,sizeof(state[socknum].recvpacket),0);
+        msectotimeval(lag,(currenttime-sendquerytime[socknum]) and tsmask);
 
-      reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);
-      state.recvpacketlen := recv(fd,state.recvpacket, SizeOf(state.recvpacket),0);
-      state.parsepacket := true;
+        if dnssyncserver = '' then reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);
+        state[socknum].recvpacketlen := recv(fd[socknum],state[socknum].recvpacket, SizeOf(state[socknum].recvpacket),0);
+        state[socknum].parsepacket := true;
+        needprocessing[socknum] := true;
+      end;
     end;
     if selectresult < 0 then exit;
     if selectresult = 0 then begin
-      {$ifdef win32}
-        currenttime := GetTickCount;
-      {$else}
-        gettimeofday(currenttime);
-      {$endif}
-      reportlag(id,-1);
-      if {$ifdef win32}(currenttime >= endtime)and ((not wrapmode) or (currenttime < starttime)) {$else}tv_compare(currenttime,endtime){$endif} {currenttime >= endtime } then begin
+
+      currenttime := getts;
+
+      if dnssyncserver = '' then reportlag(id,-1);
+      if (currenttime >= endtime) and ((not wrapmode) or (currenttime < starttime)) then begin
         exit;
       end else begin
         //resend
-        sendquery(state.sendpacket,state.sendpacketlen);
+        for socknum := numsockused-1 downto 0 do begin
+          sendquery(socknum,state[socknum].sendpacket,state[socknum].sendpacketlen);
+        end;
       end;
     end;
   until false;
 end;
+{$endif}
 
-function forwardlookup(name:string;timeout:integer):tbinip;
+procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);
+var
+  a:integer;
+  biniptemp:tbinip;
+begin
+  for a := biniplist_getcount(l2)-1 downto 0 do begin
+    biniptemp := biniplist_get(l2,a);
+    if (biniptemp.family = family) then biniplist_add(l,biniptemp);
+  end;
+end;
+
+
+function forwardlookuplist(name:string;timeout:integer):tbiniplist;
 var
   dummy : integer;
+  a,b:integer;
+  biniptemp:tbinip;
+  l:tbiniplist;
 begin
-  ipstrtobin(name,result);
-  if result.family <> 0 then exit; //it was an IP address, no need for dns
-                                   //lookup
+  ipstrtobin(name,biniptemp);
+  if biniptemp.family <> 0 then begin
+    result := biniplist_new;
+    biniplist_add(result,biniptemp);
+    exit; //it was an IP address, no need for dns
+  end;
+
   {$ifdef win32}
-    if usewindns then begin
-      result := winforwardlookup(name,false,dummy);
-      exit;
+  if usewindns then begin
+    if (useaf = useaf_v4) then a := af_inet else if (useaf = useaf_v6) then a := af_inet6 else a := 0;
+    result := winforwardlookuplist(name,a,dummy);
+    {$ifdef ipv6}
+    if (useaf = useaf_preferv4) then begin
+      {prefer mode: sort the IP's}
+      l := biniplist_new;
+      addipsoffamily(l,result,af_inet);
+      addipsoffamily(l,result,af_inet6);
+      result := l;
+    end;
+    if (useaf = useaf_preferv6) then begin
+      {prefer mode: sort the IP's}
+      l := biniplist_new;
+      addipsoffamily(l,result,af_inet6);
+      addipsoffamily(l,result,af_inet);
+      result := l;
     end;
+    {$endif}
+  end else
   {$endif}
-  setstate_forward(name,state,0);
-  resolveloop(timeout);
-  result := state.resultbin;
+  begin
+  {$ifdef syncdnscore}
+    {$ifdef linux}{$ifdef ipv6}initpreferredmode;{$endif}{$endif}
+
+    numsockused := 0;
+
+    result := biniplist_new;
+    if (useaf <> useaf_v6) then begin
+      setstate_forward(name,state[numsockused],af_inet);
+      inc(numsockused);
+    end;
+    {$ifdef ipv6}
+    if (useaf <> useaf_v4) then begin
+      setstate_forward(name,state[numsockused],af_inet6);
+      inc(numsockused);
+    end;
+    {$endif}
+
+    resolveloop(timeout);
+
+    if (numsockused = 1) then begin
+      biniplist_addlist(result,state[0].resultlist);
+    {$ifdef ipv6}
+    end else if (useaf = useaf_preferv6) then begin
+      biniplist_addlist(result,state[1].resultlist);
+      biniplist_addlist(result,state[0].resultlist);
+    end else begin
+      biniplist_addlist(result,state[0].resultlist);
+      biniplist_addlist(result,state[1].resultlist);
+    {$endif}  
+    end;
+    {$endif}
+  end;
+end;
+
+function forwardlookup(name:string;timeout:integer):tbinip;
+var
+  listtemp:tbiniplist;
+begin
+  listtemp := forwardlookuplist(name,timeout);
+  result := biniplist_get(listtemp,0);
 end;
 
 function reverselookup(ip:tbinip;timeout:integer):string;
@@ -243,11 +375,70 @@ begin
       exit;
     end;
   {$endif}
-  setstate_reverse(ip,state);
+  {$ifdef syncdnscore}
+  setstate_reverse(ip,state[0]);
+  numsockused := 1;
   resolveloop(timeout);
-  result := state.resultstr;
+  result := state[0].resultstr;
+  {$endif}
 end;
 
+{$ifdef linux}{$ifdef ipv6}{$ifdef syncdnscore}
+function getv6localips:tbiniplist;
+var
+  t:textfile;
+  s,s2:string;
+  ip:tbinip;
+  a:integer;
+begin
+  result := biniplist_new;
+
+  assignfile(t,'/proc/net/if_inet6');
+  {$i-}reset(t);{$i+}
+  if ioresult <> 0 then exit; {none found, return empty list}
+
+  while not eof(t) do begin
+    readln(t,s);
+    s2 := '';
+    for a := 0 to 7 do begin
+      if (s2 <> '') then s2 := s2 + ':';
+      s2 := s2 + copy(s,(a shl 2)+1,4);
+    end;
+    ipstrtobin(s2,ip);
+    if ip.family <> 0 then biniplist_add(result,ip);
+  end;
+  closefile(t);
+end;
+
+procedure initpreferredmode;
+var
+  l:tbiniplist;
+  a:integer;
+  ip:tbinip;
+  ipmask_global,ipmask_6to4,ipmask_teredo:tbinip;
+
+begin
+  if preferredmodeinited then exit;
+  if useaf <> useaf_default then exit;
+  useaf := useaf_preferv4;
+  l := getv6localips;
+  ipstrtobin('2000::',ipmask_global);
+  ipstrtobin('2001::',ipmask_teredo);
+  ipstrtobin('2002::',ipmask_6to4);
+  {if there is any v6 IP which is globally routable and not 6to4 and not teredo, prefer v6}
+  for a := biniplist_getcount(l)-1 downto 0 do begin
+    ip := biniplist_get(l,a);
+    if not comparebinipmask(ip,ipmask_global,3) then continue;
+    if comparebinipmask(ip,ipmask_teredo,32) then continue;
+    if comparebinipmask(ip,ipmask_6to4,16) then continue;
+    useaf := useaf_preferv6;
+    preferredmodeinited := true;
+    exit;
+  end;
+end;
+
+{$endif}{$endif}{$endif}
+
 {$ifdef win32}
   var
     wsadata : twsadata;