X-Git-Url: http://www.lcore.org/git/lcore.git/blobdiff_plain/4782a5c5afee47721cc617daa40dd29828342c2b..5ba06ea7bd30494fe55f81a3bc07c8d1eb3702e0:/dnscore.pas diff --git a/dnscore.pas b/dnscore.pas index bb4fab4..4cb52e2 100755 --- a/dnscore.pas +++ b/dnscore.pas @@ -54,25 +54,38 @@ } unit dnscore; - - {$ifdef fpc}{$mode delphi}{$endif} - - - +{$include lcoreconfig.inc} interface -uses binipstuff,classes,pgtypes; +uses binipstuff,classes,pgtypes,lcorernd; var usewindns : boolean = {$ifdef win32}true{$else}false{$endif}; -//hint to users of this unit that they should use windows dns instead. -//May be disabled by applications if desired. (e.g. if setting a custom -//dnsserverlist). +{hint to users of this unit that they should use windows dns instead. +May be disabled by applications if desired. (e.g. if setting a custom +dnsserverlist). + +note: this unit will not be able to self populate it's dns server list on +older versions of windows.} + +const + useaf_default=0; + useaf_preferv4=1; + useaf_preferv6=2; + useaf_v4=3; + useaf_v6=4; +{ +hint to users of this unit to how to deal with connecting to hostnames regarding ipv4 or ipv6 usage +can be set by apps as desired +} +var useaf:integer = useaf_default; -//note: this unit will not be able to self populate it's dns server list on -//older versions of windows. +{ +(temporarily) use a different nameserver, regardless of the dnsserverlist +} +var overridednsserver:string; const maxnamelength=127; @@ -85,12 +98,14 @@ const querytype_a=1; querytype_cname=5; querytype_aaaa=28; + querytype_a6=38; querytype_ptr=12; querytype_ns=2; querytype_soa=6; querytype_mx=15; - - maxrecursion=10; + querytype_txt=16; + querytype_spf=99; + maxrecursion=50; maxrrofakind=20; retryafter=300000; //microseconds must be less than one second; @@ -115,6 +130,7 @@ type parsepacket:boolean; resultstr:string; resultbin:tbinip; + resultlist:tbiniplist; resultaction:integer; numrr1:array[0..3] of integer; numrr2:integer; @@ -147,29 +163,27 @@ type //if you must but please document them at the same time --plugwash //function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer; -//function makereversename(const binip:tbinip):string; + +//returns the DNS name used to reverse look up an IP, such as 4.3.2.1.in-addr.arpa for 1.2.3.4 +function makereversename(const binip:tbinip):string; procedure setstate_request_init(const name:string;var state:tdnsstate); //set up state for a foward lookup. A family value of AF_INET6 will give only -//ipv6 results. Any other value will give ipv4 results in preference and ipv6 -//results if ipv4 results are not available; +//ipv6 results. Any other value will give only ipv4 results procedure setstate_forward(const name:string;var state:tdnsstate;family:integer); procedure setstate_reverse(const binip:tbinip;var state:tdnsstate); procedure setstate_failure(var state:tdnsstate); //procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate); +//for custom raw lookups such as TXT, as desired by the user +procedure setstate_custom(const name:string; requesttype:integer; var state:tdnsstate); procedure state_process(var state:tdnsstate); //function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):string; -//presumablly this is exported to allow more secure random functions -//to be substituted? -var randomfunction:function:integer; - - procedure populatednsserverlist; procedure cleardnsservercache; @@ -177,17 +191,32 @@ var dnsserverlist : tstringlist; // currentdnsserverno : integer; + +//getcurrentsystemnameserver returns the nameserver the app should use and sets +//id to the id of that nameserver. id should later be used to report how laggy +//the servers response was and if it was timed out. function getcurrentsystemnameserver(var id:integer) :string; +procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout //var // unixnameservercache:string; { $endif} -procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout +{$ifdef ipv6} +function getv6localips:tbiniplist; +procedure initpreferredmode; + +var + preferredmodeinited:boolean; + +{$endif} + var failurereason:string; +function getquerytype(s:string):integer; + implementation uses @@ -197,6 +226,24 @@ uses sysutils; + + +function getquerytype(s:string):integer; +begin + s := uppercase(s); + result := 0; + if (s = 'A') then result := querytype_a else + if (s = 'CNAME') then result := querytype_cname else + if (s = 'AAAA') then result := querytype_aaaa else + if (s = 'PTR') then result := querytype_ptr else + if (s = 'NS') then result := querytype_ns else + if (s = 'MX') then result := querytype_mx else + if (s = 'A6') then result := querytype_a6 else + if (s = 'TXT') then result := querytype_txt else + if (s = 'SOA') then result := querytype_soa else + if (s = 'SPF') then result := querytype_spf; +end; + function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer; var a,b:integer; @@ -206,7 +253,8 @@ begin { writeln('buildrequest: name: ',name);} result := 0; fillchar(packet,sizeof(packet),0); - if assigned(randomfunction) then packet.id := (randomfunction and $ffff) else packet.id := random(65536); + packet.id := randominteger($10000); + packet.flags := htons($0100); packet.rrcount[0] := htons($0001); @@ -337,25 +385,55 @@ end; {==============================================================================} -procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate); -var - a:integer; +function getrawfromrr(const rrp:trrpointer;len:integer):string; begin - state.resultaction := action_done; - state.resultstr := ''; + setlength(result,htons(trr(rrp.p^).datalen)); + uniquestring(result); + move(trr(rrp.p^).data,result[1],length(result)); +end; + + +function getipfromrr(const rrp:trrpointer;len:integer):tbinip; +begin + fillchar(result,sizeof(result),0); case trr(rrp.p^).requesttype of querytype_a: begin if htons(trr(rrp.p^).datalen) <> 4 then exit; - move(trr(rrp.p^).data,state.resultbin.ip,4); - state.resultbin.family :=AF_INET; + move(trr(rrp.p^).data,result.ip,4); + result.family :=AF_INET; end; {$ifdef ipv6} querytype_aaaa: begin if htons(trr(rrp.p^).datalen) <> 16 then exit; - state.resultbin.family := AF_INET6; - move(trr(rrp.p^).data,state.resultbin.ip6,16); + result.family := AF_INET6; + move(trr(rrp.p^).data,result.ip6,16); end; {$endif} + else + {} + end; +end; + +procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate); +var + a:integer; +begin + state.resultaction := action_done; + state.resultstr := ''; + case trr(rrp.p^).requesttype of + querytype_a{$ifdef ipv6},querytype_aaaa{$endif}: begin + state.resultbin := getipfromrr(rrp,len); + end; + querytype_txt:begin + {TXT returns a raw string} + state.resultstr := copy(getrawfromrr(rrp,len),2,9999); + fillchar(state.resultbin,sizeof(state.resultbin),0); + end; + querytype_mx:begin + {MX is a name after a 16 bits word} + state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+12,0,a); + fillchar(state.resultbin,sizeof(state.resultbin),0); + end; else {other reply types (PTR, MX) return a hostname} state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+10,0,a); @@ -390,6 +468,13 @@ begin state.requesttype := querytype_ptr; end; +procedure setstate_custom(const name:string; requesttype:integer; var state:tdnsstate); +begin + setstate_request_init(name,state); + state.requesttype := requesttype; +end; + + procedure setstate_failure(var state:tdnsstate); begin state.resultstr := ''; @@ -456,6 +541,19 @@ begin goto failure; end; + {if we requested A or AAAA build a list of all replies} + if (state.requesttype = querytype_a) or (state.requesttype = querytype_aaaa) then begin + state.resultlist := biniplist_new; + for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin + rrptemp := @state.rrdata[1+a*sizeof(trrpointer)]; + rrtemp := rrptemp.p; + b := rrptemp.len; + if rrtemp.requesttype = state.requesttype then begin + biniplist_add(state.resultlist,getipfromrr(rrptemp^,b)); + end; + end; + end; + {- check for items of the requested type in answer section, if so return success first} for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin rrptemp := @state.rrdata[1+a*sizeof(trrpointer)]; @@ -480,23 +578,7 @@ begin {no cnames found, no items of correct type found} if state.forwardfamily <> 0 then goto failure; -{$ifdef ipv6} - if (state.requesttype = querytype_a) then begin - {v6 only: in case of forward, look for AAAA in alternative section} - for a := state.numrr1[0]+state.numrr1[1]+state.numrr1[2] to (state.numrr2-1) do begin - rrptemp := @state.rrdata[1+a*sizeof(trrpointer)]; - rrtemp := rrptemp.p; - b := rrptemp.len; - if rrtemp.requesttype = querytype_aaaa then begin - setstate_return(rrptemp^,b,state); - exit; - end; - end; - {no AAAA's found in alternative, do a recursive lookup for them} - state.requesttype := querytype_aaaa; - goto recursed; - end; -{$endif} + goto failure; recursed: {here it needs recursed lookup} @@ -587,6 +669,7 @@ begin {$ifdef win32} if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll'); if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams'); + if not assigned(getnetworkparams) then exit; fixed_info_len := 0; if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit; //fixed_info_len :=sizeof(tfixed_info); @@ -635,7 +718,7 @@ begin end; function getcurrentsystemnameserver(var id:integer):string; -var +var counter : integer; begin @@ -666,6 +749,75 @@ begin end; + + +{$ifdef ipv6} + +{$ifdef linux} +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; + +{$else} +function getv6localips:tbiniplist; +begin + result := biniplist_new; +end; +{$endif} + +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; + l := getv6localips; + if biniplist_getcount(l) = 0 then exit; + useaf := useaf_preferv4; + 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} + + { quick and dirty description of dns packet structure to aid writing and understanding of parser code, refer to appropriate RFCs for proper specs - all words are network order