//not seem to have any form of retry code.\r
\r
unit dnsasync;\r
-\r
+{$ifdef fpc}\r
+ {$mode delphi}\r
+{$endif}\r
interface\r
\r
uses\r
- {$ifdef win32}\r
+ {$ifdef winasyncdns}\r
dnswin,\r
{$endif}\r
lsocket,lcore,\r
- classes,binipstuff,dnscore,btime;\r
+ classes,binipstuff,dnscore,btime,lcorernd;\r
+\r
+{$include lcoreconfig.inc}\r
\r
const\r
numsock=1{$ifdef ipv6}+1{$endif};\r
\r
states: array[0..numsock-1] of tdnsstate;\r
\r
+ destinations: array[0..numsock-1] of tbinip;\r
+\r
dnsserverids : array[0..numsock-1] of integer;\r
startts:double;\r
- {$ifdef win32}\r
+ {$ifdef winasyncdns}\r
dwas : tdnswinasync;\r
{$endif}\r
\r
procedure asyncprocess(socketno:integer);\r
procedure receivehandler(sender:tobject;error:word);\r
function sendquery(socketno:integer;const packet:tdnspacket;len:integer):boolean;\r
- {$ifdef win32}\r
+ {$ifdef winasyncdns}\r
procedure winrequestdone(sender:tobject;error:word);\r
{$endif}\r
\r
onrequestdone:tsocketevent;\r
\r
//addr and port allow the application to specify a dns server specifically\r
- //for this dnsasync object. This is not a reccomended mode of operation\r
+ //for this dnsasync object. This is not a recommended mode of operation\r
//because it limits the app to one dns server but is kept for compatibility\r
//and special uses.\r
- addr,port:string;\r
+ addr,port:ansistring;\r
\r
overrideaf : integer;\r
\r
- //A family value of AF_INET6 will give only\r
- //ipv6 results. Any other value will give ipv4 results in preference and ipv6\r
- //results if ipv4 results are not available;\r
- forwardfamily:integer;\r
-\r
procedure cancel;//cancel an outstanding dns request\r
- function dnsresult:string; //get result of dnslookup as a string\r
+ function dnsresult:ansistring; //get result of dnslookup as a string\r
procedure dnsresultbin(var binip:tbinip); //get result of dnslookup as a tbinip\r
property dnsresultlist : tbiniplist read fresultlist;\r
- procedure forwardlookup(const name:string); //start forward lookup,\r
- //preffering ipv4\r
+ procedure forwardlookup(const name:ansistring); //start forward lookup,\r
+ //preferring ipv4\r
procedure reverselookup(const binip:tbinip); //start reverse lookup\r
+ procedure customlookup(const name:ansistring;querytype:integer); //start custom type lookup\r
\r
constructor create(aowner:tcomponent); override;\r
destructor destroy; override;\r
socketno : integer;\r
begin\r
for socketno := 0 to numsock -1 do begin\r
- if dnsserverids[socketno] >= 0 then begin\r
- reportlag(dnsserverids[socketno],-1);\r
- dnsserverids[socketno] := -1;\r
+ if assigned(sockets[socketno]) then begin\r
+ if dnsserverids[socketno] >= 0 then begin\r
+ reportlag(dnsserverids[socketno],-1);\r
+ dnsserverids[socketno] := -1;\r
+ end;\r
+ sockets[socketno].release;\r
+ setstate_request_init('',states[socketno]);\r
end;\r
- sockets[socketno].release;\r
- setstate_request_init('',states[socketno]);\r
end;\r
+\r
+ {$ifdef winasyncdns}\r
+ if assigned(dwas) then begin\r
+ dwas.release;\r
+ dwas := nil;\r
+ end;\r
+ {$endif}\r
+\r
inherited destroy;\r
end;\r
\r
procedure tdnsasync.receivehandler(sender:tobject;error:word);\r
var\r
socketno : integer;\r
+ Src : TInetSockAddrV;\r
+ SrcLen : Integer;\r
+ fromip:tbinip;\r
+ fromport:ansistring;\r
begin\r
socketno := tlsocket(sender).tag;\r
//writeln('got a reply on socket number ',socketno);\r
fillchar(states[socketno].recvpacket,sizeof(states[socketno].recvpacket),0);\r
- states[socketno].recvpacketlen := twsocket(sender).Receive(@(states[socketno].recvpacket), SizeOf(states[socketno].recvpacket));\r
+\r
+ SrcLen := SizeOf(Src);\r
+ states[socketno].recvpacketlen := twsocket(sender).ReceiveFrom(@(states[socketno].recvpacket), SizeOf(states[socketno].recvpacket), Src, SrcLen);\r
+\r
+ fromip := inaddrvtobinip(Src);\r
+ fromport := inttostr(htons(src.InAddr.port));\r
+\r
+ if ((not comparebinip(fromip,destinations[socketno])) or (fromport <> port)) then begin\r
+ // writeln('dnsasync received from wrong IP:port ',ipbintostr(fromip),'#',fromport,', expected ',ipbintostr(destinations[socketno]),'#',port);\r
+ exit;\r
+ end;\r
+\r
states[socketno].parsepacket := true;\r
if states[socketno].resultaction <> action_done then begin\r
//we ignore packets that come after we are done\r
if dnsserverids[socketno] >= 0 then begin\r
- reportlag(dnsserverids[socketno],trunc((unixtimefloat-startts)*1000));\r
+ reportlag(dnsserverids[socketno],trunc((unixtimefloat-startts)*1000000));\r
dnsserverids[socketno] := -1;\r
end;\r
{ writeln('received reply');}\r
\r
function tdnsasync.sendquery(socketno:integer;const packet:tdnspacket;len:integer):boolean;\r
var\r
- destination : string;\r
+ destination : tbinip;\r
inaddr : tinetsockaddrv;\r
+ trytolisten:integer;\r
begin\r
{ writeln('sendquery ',decodename(state.packet,state.packetlen,12,0,a),' ',state.requesttype);}\r
//writeln('trying to send query on socket number ',socketno);\r
if port = '' then port := '53';\r
sockets[socketno].Proto := 'udp';\r
sockets[socketno].ondataavailable := receivehandler;\r
- try\r
- sockets[socketno].listen;\r
- except\r
- result := false;\r
- exit;\r
+\r
+ {we are going to bind on a random local port for the DNS request, against the kaminsky attack\r
+ there is a small chance that we're trying to bind on an already used port, so retry a few times}\r
+ for trytolisten := 3 downto 0 do begin\r
+ try\r
+ sockets[socketno].port := inttostr(1024 + randominteger(65536 - 1024));\r
+ sockets[socketno].listen;\r
+ except\r
+ {writeln('failed to listen ',sockets[socketno].localport,' ',trytolisten);}\r
+ if (trytolisten = 0) then begin\r
+ result := false;\r
+ exit;\r
+ end;\r
+ end;\r
end;\r
\r
end;\r
if addr <> '' then begin\r
dnsserverids[socketno] := -1;\r
- destination := addr\r
+ destination := ipstrtobinf(addr);\r
end else begin\r
- destination := getcurrentsystemnameserver(dnsserverids[socketno]);\r
+ destination := getcurrentsystemnameserverbin(dnsserverids[socketno]);\r
end;\r
- makeinaddrv(ipstrtobinf(destination),port,inaddr);\r
+ destinations[socketno] := destination;\r
+\r
+ {$ifdef ipv6}{$ifdef mswindows}\r
+ if destinations[socketno].family = AF_INET6 then if (requestaf = useaf_default) then requestaf := useaf_preferv6;\r
+ {$endif}{$endif}\r
+\r
+ makeinaddrv(destinations[socketno],port,inaddr);\r
sockets[socketno].sendto(inaddr,sizeof(inaddr), @packet,len);\r
result := true;\r
\r
bip : tbinip;\r
i : integer;\r
begin\r
-\r
ipstrtobin(name,bip);\r
\r
if bip.family <> 0 then begin\r
end;\r
\r
if overrideaf = useaf_default then begin\r
- {$ifdef linux}{$ifdef ipv6}initpreferredmode;{$endif}{$endif}\r
+ {$ifdef ipv6}\r
+ {$ifdef mswindows}if not (usewindns and (addr = '')) then{$endif}\r
+ initpreferredmode;\r
+ {$endif}\r
requestaf := useaf;\r
end else begin\r
requestaf := overrideaf;\r
end;\r
\r
- {$ifdef win32}\r
- if usewindns or (addr = '') then begin\r
+ {$ifdef winasyncdns}\r
+ if usewindns and (addr = '') then begin\r
dwas := tdnswinasync.create;\r
dwas.onrequestdone := winrequestdone;\r
- if forwardfamily = AF_INET6 then begin\r
- dwas.forwardlookup(name,true);\r
- end else begin\r
- dwas.forwardlookup(name,false);\r
- end;\r
+\r
+ dwas.forwardlookup(name);\r
+\r
exit;\r
end;\r
{$endif}\r
inc(numsockused);\r
end;\r
{$endif}\r
+\r
for i := 0 to numsockused-1 do begin\r
asyncprocess(i);\r
end;\r
-\r
end;\r
\r
procedure tdnsasync.reverselookup;\r
-\r
begin\r
- {$ifdef win32}\r
- if usewindns or (addr = '') then begin\r
+ {$ifdef winasyncdns}\r
+ if usewindns and (addr = '') then begin\r
dwas := tdnswinasync.create;\r
dwas.onrequestdone := winrequestdone;\r
dwas.reverselookup(binip);\r
asyncprocess(0);\r
end;\r
\r
+procedure tdnsasync.customlookup;\r
+begin\r
+ setstate_custom(name,querytype,states[0]);\r
+ numsockused := 1;\r
+ asyncprocess(0);\r
+end;\r
+\r
function tdnsasync.dnsresult;\r
begin\r
if states[0].resultstr <> '' then result := states[0].resultstr else begin\r
var\r
socketno : integer;\r
begin\r
- {$ifdef win32}\r
+ {$ifdef winasyncdns}\r
if assigned(dwas) then begin\r
dwas.release;\r
dwas := nil;\r
onrequestdone(self,0);\r
end;\r
\r
-{$ifdef win32}\r
+{$ifdef winasyncdns}\r
procedure tdnsasync.winrequestdone(sender:tobject;error:word);\r
\r
begin\r
- if dwas.reverse then begin \r
+ if dwas.reverse then begin\r
states[0].resultstr := dwas.name;\r
end else begin \r
\r