{$mode delphi}\r
{$endif}\r
\r
+{$include lcoreconfig.inc}\r
+\r
interface\r
uses\r
dnscore,\r
binipstuff,\r
- {$ifdef win32}\r
+ {$ifdef mswindows}\r
winsock,\r
windows,\r
{$else}\r
sockets,\r
fd_utils,\r
{$endif}\r
- sysutils;\r
+ lcorernd,\r
+ sysutils,\r
+ ltimevalstuff;\r
\r
//convert a name to an IP\r
-//IPV4 is preffered if availible, if not IPV6 will be returned (if ipv6 support\r
-//compiled in)\r
-//on error the binip will have a family of 0 (other fiels are also currently\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 fields are also currently\r
//zeroed out but may be used for further error information in future)\r
-//timeout is in seconds, 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:ansistring;timeout:integer):tbiniplist;\r
\r
\r
-//convert an IP to a name, on error a null string will be returned, other \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
-var\r
- dnssyncserver:string;\r
- id : integer;\r
- {$ifdef win32}\r
- sendquerytime : integer;\r
- {$else}\r
- sendquerytime : ttimeval;\r
- {$endif}\r
+\r
+const\r
+ tswrap=$4000;\r
+ tsmask=tswrap-1;\r
+\r
+ numsock=1{$ifdef ipv6}+1{$endif};\r
+ defaulttimeout=10000;\r
+ const mintimeout=16;\r
+\r
+ toport='53';\r
+\r
implementation\r
-{$ifdef win32}\r
+\r
+{$ifdef mswindows}\r
uses dnswin;\r
{$endif}\r
\r
+\r
+{$ifndef mswindows}\r
+{$define syncdnscore}\r
+{$endif}\r
+\r
{$i unixstuff.inc}\r
-{$i ltimevalstuff.inc}\r
\r
-var\r
- fd:integer;\r
- state:tdnsstate;\r
-{$ifdef win32}\r
+type tdnsstatearr=array[0..numsock-1] of tdnsstate;\r
+\r
+{$ifdef syncdnscore}\r
+\r
+\r
+{$ifdef mswindows}\r
const\r
winsocket = 'wsock32.dll';\r
- function sendto(s: TSocket; const Buf; len, flags: Integer; var addrto: TinetSockAddr; tolen: Integer): Integer; stdcall; external winsocket name 'sendto';\r
- function bind(s: TSocket; var addr: TinetSockAddr; namelen: Integer): Longbool; stdcall; external winsocket name 'bind';\r
+ function sendto(s: TSocket; const Buf; len, flags: Integer; var addrto: TinetSockAddrV; tolen: Integer): Integer; stdcall; external winsocket name 'sendto';\r
+ function bind(s: TSocket; var addr: TinetSockAddrV; namelen: Integer): Longbool; stdcall; external winsocket name 'bind';\r
type\r
fdset=tfdset;\r
{$endif}\r
\r
-function sendquery(const packet:tdnspacket;len:integer):boolean;\r
-var\r
- a:integer;\r
- addr : string;\r
- port : string;\r
- inaddr : TInetSockAddr;\r
\r
+function getts:integer;\r
+{$ifdef mswindows}\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
+ result := GetTickCount and tsmask;\r
+{$else}\r
+var\r
+ temp:ttimeval;\r
+begin\r
+ gettimemonotonic(temp);\r
+ result := ((temp.tv_usec div 1000) + (temp.tv_sec * 1000)) and tsmask;\r
+{$endif}\r
+end;\r
\r
- if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);\r
- port := '53';\r
+procedure resolveloop(timeout:integer;var state:tdnsstatearr;numsockused:integer);\r
+var\r
+ selectresult : integer;\r
+ fds : fdset;\r
\r
- inAddr.family:=AF_INET;\r
- inAddr.port:=htons(strtointdef(port,0));\r
- inAddr.addr:=htonl(longip(addr));\r
+ endtime : longint;\r
+ starttime : longint;\r
+ wrapmode : boolean;\r
+ currenttime : integer;\r
+\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
+ 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,packet,len,0,inaddr,sizeof(inaddr));\r
- {$ifdef win32}\r
- sendquerytime := GetTickCount and $3fff;\r
- {$else}\r
- gettimeofday(sendquerytime);\r
- {$endif}\r
- result := true;\r
-end;\r
\r
procedure setupsocket;\r
var\r
- inAddrtemp : TInetSockAddr;\r
+ inAddrtemp : TInetSockAddrV;\r
+ biniptemp:tbinip;\r
+ a,retrycount,porttemp:integer;\r
+ bindresult:boolean;\r
begin\r
- if fd > 0 then exit;\r
-\r
- fd := Socket(AF_INET,SOCK_DGRAM,0);\r
- inAddrtemp.family:=AF_INET;\r
- inAddrtemp.port:=0;\r
- inAddrtemp.addr:=0;{htonl(longip('0.0.0.0'));}\r
- If {$ifndef win32}Not{$endif} Bind(fd,inAddrtemp,SizeOf(inAddrtemp)) Then begin\r
- {$ifdef win32}\r
- raise Exception.create('unable to bind '+inttostr(WSAGetLastError));\r
- {$else}\r
- raise Exception.create('unable to bind '+inttostr(socketError));\r
- {$endif}\r
+ biniptemp := getcurrentsystemnameserverbin(id);\r
+ //must get the DNS server here so we know to init v4 or v6\r
+\r
+ if biniptemp.family = AF_INET6 then biniptemp := ipstrtobinf('::') else biniptemp := ipstrtobinf('0.0.0.0');\r
+\r
+\r
+ for a := 0 to numsockused-1 do begin\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 (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
+ {$endif}\r
+ end;\r
end;\r
end;\r
\r
-procedure resolveloop(timeout:integer);\r
+procedure cleanupsockets;\r
var\r
- selectresult : integer;\r
- fds : fdset;\r
- {$ifdef win32}\r
- endtime : longint;\r
- starttime : longint;\r
- wrapmode : boolean;\r
- currenttime : integer;\r
- {$else}\r
- endtime : ttimeval;\r
- currenttime : ttimeval;\r
+ a:integer;\r
+begin\r
+ for a := 0 to numsockused-1 do closesocket(fd[a]);\r
+end;\r
\r
- {$endif}\r
- lag : ttimeval;\r
- currenttimeout : ttimeval;\r
- selecttimeout : ttimeval;\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
+ 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
- {$ifdef win32}\r
- starttime := GetTickCount and $3fff;\r
- endtime := starttime +(timeout*1000);\r
- if (endtime and $4000)=0 then begin\r
- wrapmode := false;\r
- end else begin\r
- wrapmode := true;\r
- end;\r
- endtime := endtime and $3fff;\r
- {$else}\r
- gettimeofday(endtime);\r
- endtime.tv_sec := endtime.tv_sec + timeout;\r
- {$endif}\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
\r
setupsocket;\r
+\r
+\r
+ for socknum := 0 to numsockused-1 do begin\r
+ needprocessing[socknum] := true;\r
+ finished[socknum] := false;\r
+ end;\r
+\r
repeat\r
- state_process(state);\r
- case state.resultaction of\r
- action_ignore: begin\r
-{ writeln('ignore');}\r
- {do nothing}\r
- end;\r
- action_done: begin\r
-{ writeln('done');}\r
- exit;\r
- //onrequestdone(self,0);\r
- end;\r
- action_sendquery:begin\r
+ for socknum := numsockused-1 downto 0 do if needprocessing[socknum] then begin\r
+ state_process(state[socknum]);\r
+ case state[socknum].resultaction of\r
+ action_ignore: begin\r
+ {do nothing}\r
+ end;\r
+ action_done: begin\r
+ finished[socknum] := true;\r
+ //exit if all resolvers are finished\r
+ b := 0;\r
+ for a := 0 to numsockused-1 do begin\r
+ 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
+ end;\r
+ action_sendquery:begin\r
{ writeln('send query');}\r
- sendquery(state.sendpacket,state.sendpacketlen);\r
+ sendquery(socknum,state[socknum].sendpacket,state[socknum].sendpacketlen);\r
+ end;\r
end;\r
+ needprocessing[socknum] := false;\r
end;\r
- {$ifdef win32}\r
- currenttime := GetTickCount and $3fff;\r
- msectotimeval(selecttimeout, (endtime-currenttime)and$3fff);\r
- {$else}\r
- gettimeofday(currenttime);\r
- selecttimeout := endtime;\r
- tv_substract(selecttimeout,currenttime);\r
- {$endif}\r
+\r
+ currenttime := getts;\r
+ msectotimeval(selecttimeout, (endtime-currenttime) and tsmask);\r
+\r
fd_zero(fds);\r
- fd_set(fd,fds);\r
+ for socknum := numsockused-1 downto 0 do if not finished[socknum] then fd_set(fd[socknum],fds);\r
if (selecttimeout.tv_sec > 0) or (selecttimeout.tv_usec > retryafter) then begin\r
selecttimeout.tv_sec := 0;\r
selecttimeout.tv_usec := retryafter;\r
end;\r
- selectresult := select(fd+1,@fds,nil,nil,@selecttimeout);\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
if selectresult > 0 then begin\r
-{ writeln('selectresult>0');}\r
- //why the hell are we zeroing out the packet buffer before reading into it?! --plugwash\r
- fillchar(state.recvpacket,sizeof(state.recvpacket),0);\r
- {$ifdef win32}\r
- msectotimeval(lag,(currenttime-sendquerytime)and$3fff);\r
- {$else}\r
- lag := currenttime;\r
- tv_substract(lag,sendquerytime);\r
+ currenttime := getts;\r
+ for socknum := numsockused-1 downto 0 do if fd_isset(fd[socknum],fds) then begin\r
+ { writeln('selectresult>0');}\r
+ //why the hell are we zeroing out the packet buffer before reading into it?! --plugwash\r
\r
- {$endif}\r
+ fillchar(state[socknum].recvpacket,sizeof(state[socknum].recvpacket),0);\r
+ msectotimeval(lag,(currenttime-sendquerytime[socknum]) and tsmask);\r
+\r
+ reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);\r
\r
- reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);\r
- state.recvpacketlen := recv(fd,state.recvpacket, SizeOf(state.recvpacket),0);\r
- state.parsepacket := true;\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
if selectresult = 0 then begin\r
- {$ifdef win32}\r
- currenttime := GetTickCount;\r
- {$else}\r
- gettimeofday(currenttime);\r
- {$endif}\r
+\r
+ currenttime := getts;\r
+\r
reportlag(id,-1);\r
- if {$ifdef win32}(currenttime >= endtime)and ((not wrapmode) or (currenttime < starttime)) {$else}tv_compare(currenttime,endtime){$endif} {currenttime >= endtime } then begin\r
+ if (currenttime >= endtime) and ((not wrapmode) or (currenttime < starttime)) then begin\r
+ cleanupsockets;\r
exit;\r
end else begin\r
//resend\r
- sendquery(state.sendpacket,state.sendpacketlen);\r
+ for socknum := numsockused-1 downto 0 do begin\r
+ sendquery(socknum,state[socknum].sendpacket,state[socknum].sendpacketlen);\r
+ end;\r
end;\r
end;\r
until false;\r
end;\r
+{$endif}\r
+\r
+\r
\r
-function forwardlookup(name:string;timeout:integer):tbinip;\r
+function forwardlookuplist(name:ansistring;timeout:integer):tbiniplist;\r
var\r
dummy : integer;\r
+ a:integer;\r
+ biniptemp:tbinip;\r
+ l:tbiniplist;\r
+\r
+ numsockused:integer;\r
+ state:tdnsstatearr;\r
+\r
begin\r
- ipstrtobin(name,result);\r
- if result.family <> 0 then exit; //it was an IP address, no need for dns\r
- //lookup\r
- {$ifdef win32}\r
- if usewindns then begin\r
- result := winforwardlookup(name,false,dummy);\r
- exit;\r
+ ipstrtobin(name,biniptemp);\r
+ if biniptemp.family <> 0 then begin\r
+ result := biniplist_new;\r
+ biniplist_add(result,biniptemp);\r
+ exit; //it was an IP address, no need for dns\r
+ end;\r
+\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
+ {$ifdef ipv6}\r
+ if (useaf = useaf_preferv4) then begin\r
+ {prefer mode: sort the IP's}\r
+ l := biniplist_new;\r
+ addipsoffamily(l,result,af_inet);\r
+ addipsoffamily(l,result,af_inet6);\r
+ result := l;\r
+ end;\r
+ if (useaf = useaf_preferv6) then begin\r
+ {prefer mode: sort the IP's}\r
+ l := biniplist_new;\r
+ addipsoffamily(l,result,af_inet6);\r
+ addipsoffamily(l,result,af_inet);\r
+ result := l;\r
end;\r
+ {$endif}\r
+ end else\r
{$endif}\r
- setstate_forward(name,state,0);\r
- resolveloop(timeout);\r
- result := state.resultbin;\r
+ begin\r
+ {$ifdef syncdnscore}\r
+ {$ifdef ipv6}initpreferredmode;{$endif}\r
+\r
+ numsockused := 0;\r
+\r
+ result := biniplist_new;\r
+ if (useaf <> useaf_v6) then begin\r
+ setstate_forward(name,state[numsockused],af_inet);\r
+ inc(numsockused);\r
+ end;\r
+ {$ifdef ipv6}\r
+ if (useaf <> useaf_v4) then begin\r
+ setstate_forward(name,state[numsockused],af_inet6);\r
+ inc(numsockused);\r
+ end;\r
+ {$endif}\r
+\r
+ resolveloop(timeout,state,numsockused);\r
+\r
+ if (numsockused = 1) then begin\r
+ biniplist_addlist(result,state[0].resultlist);\r
+ {$ifdef ipv6}\r
+ end else if (useaf = useaf_preferv6) then begin\r
+ biniplist_addlist(result,state[1].resultlist);\r
+ biniplist_addlist(result,state[0].resultlist);\r
+ end else begin\r
+ biniplist_addlist(result,state[0].resultlist);\r
+ biniplist_addlist(result,state[1].resultlist);\r
+ {$endif}\r
+ end;\r
+ {$endif}\r
+ end;\r
end;\r
\r
-function reverselookup(ip:tbinip;timeout:integer):string;\r
+function forwardlookup(name:ansistring;timeout:integer):tbinip;\r
+var\r
+ listtemp:tbiniplist;\r
+begin\r
+ listtemp := forwardlookuplist(name,timeout);\r
+ result := biniplist_get(listtemp,0);\r
+end;\r
+\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
end;\r
{$endif}\r
- setstate_reverse(ip,state);\r
- resolveloop(timeout);\r
- result := state.resultstr;\r
+ {$ifdef syncdnscore}\r
+ setstate_reverse(ip,state[0]);\r
+ numsockused := 1;\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