X-Git-Url: http://www.lcore.org/git/lcore.git/blobdiff_plain/d53fe26eaac895d1e7a0ba2b2b8965cf77932de8..51075d051580863ca073aa91883357410b358e40:/dnscore.pas?ds=sidebyside diff --git a/dnscore.pas b/dnscore.pas old mode 100755 new mode 100644 index 600581d..e38f35f --- a/dnscore.pas +++ b/dnscore.pas @@ -6,12 +6,12 @@ { code wanting to use this dns system should act as follows (note: app - developers will probablly want to use dnsasync or dnssync or write a similar - wrapper unit of thier own). + developers will probably want to use dnsasync or dnssync or write a similar + wrapper unit of their own). for normal lookups call setstate_forward or setstate_reverse to set up the state, for more obscure lookups use setstate_request_init and fill in other - relavent state manually. + relevant state manually. call state_process which will do processing on the information in the state and return an action @@ -20,7 +20,7 @@ action_sendpacket means that dnscore wants the code that calls it to send the packet in sendpacket/sendpacketlen and then start (or go back to) listening for - action_done means the request has completed (either suceeded or failed) + action_done means the request has completed (either succeeded or failed) callers should resend the last packet they tried to send if they have not been asked to send a new packet for more than some timeout value they choose. @@ -28,27 +28,27 @@ when a packet is received the application should put the packet in recvbuf/recvbuflen , set state.parsepacket and call state_process again - once the app gets action_done it can determine sucess or failure in the + once the app gets action_done it can determine success or failure in the following ways. on failure state.resultstr will be an empty string and state.resultbin will - be zeroed out (easilly detected by the fact that it will have a family of 0) + be zeroed out (easily detected by the fact that it will have a family of 0) on success for a A or AAAA lookup state.resultstr will be an empty string - and state.resultbin will contain the result (note: AAAA lookups require IPV6 + and state.resultbin will contain the result (note: AAAA lookups require IPv6 enabled). - if an A lookup fails and the code is built with ipv6 enabled then the code + if an A lookup fails and the code is built with IPv6 enabled then the code will return any AAAA records with the same name. The reverse does not apply - so if an application preffers IPV6 but wants IPV4 results as well it must - check them seperately. + so if an application prefers IPv6 but wants IPv4 results as well it must + check them separately. on success for any other type of lookup state.resultstr will be an empty note the state contains ansistrings, setstate_init with a null name parameter - can be used to clean theese up if required. + can be used to clean these up if required. - callers may use setstate_failure to mark the state as failed themseleves + callers may use setstate_failure to mark the state as failed themselves before passing it on to other code, for example this may be done in the event of a timeout. } @@ -60,9 +60,9 @@ unit dnscore; interface -uses binipstuff,classes,pgtypes; +uses binipstuff,classes,pgtypes,lcorernd; -var usewindns : boolean = {$ifdef win32}true{$else}false{$endif}; +var usewindns : boolean = {$ifdef mswindows}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). @@ -82,6 +82,11 @@ can be set by apps as desired } var useaf:integer = useaf_default; +{ +(temporarily) use a different nameserver, regardless of the dnsserverlist +} +var overridednsserver:ansistring; + const maxnamelength=127; maxnamefieldlen=63; @@ -93,13 +98,18 @@ 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; - maxrrofakind=20; + querytype_txt=16; + querytype_spf=99; + maxrecursion=50; + maxrrofakind=32; + {the maximum number of RR of a kind of purely an extra sanity check and could be omitted. + before, i set it to 20, but valid replies can have more. dnscore only does udp requests, + and ordinary DNS, so up to 512 bytes. the maximum number of A records that fits seems to be 29} retryafter=300000; //microseconds must be less than one second; timeoutlag=1000000000; // penalty value to be treated as lag in the event of a timeout (microseconds) @@ -118,16 +128,16 @@ type tdnsstate=record id:word; recursioncount:integer; - queryname:string; + queryname:ansistring; requesttype:word; parsepacket:boolean; - resultstr:string; + resultstr:ansistring; resultbin:tbinip; resultlist:tbiniplist; resultaction:integer; numrr1:array[0..3] of integer; numrr2:integer; - rrdata:string; + rrdata:ansistring; sendpacketlen:integer; sendpacket:tdnspacket; recvpacketlen:integer; @@ -152,46 +162,45 @@ type end; //commenting out functions from interface that do not have documented semantics -//and probablly should not be called from outside this unit, reenable them +//and probably should not be called from outside this unit, reenable them //if you must but please document them at the same time --plugwash //function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer; //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; +function makereversename(const binip:tbinip):ansistring; -procedure setstate_request_init(const name:string;var state:tdnsstate); +procedure setstate_request_init(const name:ansistring;var state:tdnsstate); -//set up state for a foward lookup. A family value of AF_INET6 will give only +//set up state for a forward lookup. A family value of AF_INET6 will give only //ipv6 results. Any other value will give only ipv4 results -procedure setstate_forward(const name:string;var state:tdnsstate;family:integer); +procedure setstate_forward(const name:ansistring;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:ansistring; 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; var - dnsserverlist : tstringlist; + dnsserverlist : tbiniplist; + dnsserverlag:tlist; // 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; +function getcurrentsystemnameserver(var id:integer) :ansistring; +function getcurrentsystemnameserverbin(var id:integer) :tbinip; procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout //var @@ -199,37 +208,54 @@ procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and { $endif} -{$ifdef linux}{$ifdef ipv6} -function getv6localips:tbiniplist; +{$ifdef ipv6} procedure initpreferredmode; var preferredmodeinited:boolean; -{$endif}{$endif} +{$endif} var - failurereason:string; + failurereason:ansistring; + +function getquerytype(s:ansistring):integer; implementation uses - {$ifdef win32} - windows, - {$endif} - + lcorelocalips, sysutils; -function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer; + + +function getquerytype(s:ansistring):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:ansistring;var packet:tdnspacket;requesttype:word):integer; var a,b:integer; - s:string; + s:ansistring; arr:array[0..sizeof(packet)-1] of byte absolute packet; 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); @@ -264,9 +290,9 @@ begin arr[result-4] := requesttype shr 8; end; -function makereversename(const binip:tbinip):string; +function makereversename(const binip:tbinip):ansistring; var - name:string; + name:ansistring; a,b:integer; begin name := ''; @@ -298,10 +324,10 @@ doesnt read beyond len. empty result + non null failurereason: failure empty result + null failurereason: internal use } -function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):string; +function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):ansistring; var arr:array[0..sizeof(packet)-1] of byte absolute packet; - s:string; + s:ansistring; a,b:integer; begin numread := 0; @@ -342,7 +368,7 @@ begin failurereason := 'decoding name: got out of range2'; exit; end; - result := result + char(arr[a]); + result := result + ansichar(arr[a]); end; inc(numread,b+1); @@ -360,6 +386,14 @@ end; {==============================================================================} +function getrawfromrr(const rrp:trrpointer;len:integer):ansistring; +begin + 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); @@ -391,6 +425,16 @@ begin 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); @@ -398,7 +442,7 @@ begin end; end; -procedure setstate_request_init(const name:string;var state:tdnsstate); +procedure setstate_request_init(const name:ansistring;var state:tdnsstate); begin {destroy things properly} state.resultstr := ''; @@ -409,7 +453,7 @@ begin state.parsepacket := false; end; -procedure setstate_forward(const name:string;var state:tdnsstate;family:integer); +procedure setstate_forward(const name:ansistring;var state:tdnsstate;family:integer); begin setstate_request_init(name,state); state.forwardfamily := family; @@ -425,6 +469,13 @@ begin state.requesttype := querytype_ptr; end; +procedure setstate_custom(const name:ansistring; requesttype:integer; var state:tdnsstate); +begin + setstate_request_init(name,state); + state.requesttype := requesttype; +end; + + procedure setstate_failure(var state:tdnsstate); begin state.resultstr := ''; @@ -454,7 +505,10 @@ begin state.numrr2 := 0; for a := 0 to 3 do begin state.numrr1[a] := htons(state.recvpacket.rrcount[a]); - if state.numrr1[a] > maxrrofakind then goto failure; + if state.numrr1[a] > maxrrofakind then begin + failurereason := 'exceeded maximum RR of a kind'; + goto failure; + end; inc(state.numrr2,state.numrr1[a]); end; @@ -543,7 +597,7 @@ recursed: goto failure; end; - {do /ets/hosts lookup here} + {do /etc/hosts lookup here} state.sendpacketlen := buildrequest(state.queryname,state.sendpacket,state.requesttype); if state.sendpacketlen = 0 then begin failurereason := 'building request packet failed'; @@ -556,132 +610,59 @@ recursed: failure: setstate_failure(state); end; -{$ifdef win32} - const - MAX_HOSTNAME_LEN = 132; - MAX_DOMAIN_NAME_LEN = 132; - MAX_SCOPE_ID_LEN = 260 ; - MAX_ADAPTER_NAME_LENGTH = 260; - MAX_ADAPTER_ADDRESS_LENGTH = 8; - MAX_ADAPTER_DESCRIPTION_LENGTH = 132; - ERROR_BUFFER_OVERFLOW = 111; - MIB_IF_TYPE_ETHERNET = 6; - MIB_IF_TYPE_TOKENRING = 9; - MIB_IF_TYPE_FDDI = 15; - MIB_IF_TYPE_PPP = 23; - MIB_IF_TYPE_LOOPBACK = 24; - MIB_IF_TYPE_SLIP = 28; - - - type - tip_addr_string=packed record - Next :pointer; - IpAddress : array[0..15] of char; - ipmask : array[0..15] of char; - context : dword; - end; - pip_addr_string=^tip_addr_string; - tFIXED_INFO=packed record - HostName : array[0..MAX_HOSTNAME_LEN-1] of char; - DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of char; - currentdnsserver : pip_addr_string; - dnsserverlist : tip_addr_string; - nodetype : longint; - ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of char; - enablerouting : longbool; - enableproxy : longbool; - enabledns : longbool; - end; - pFIXED_INFO=^tFIXED_INFO; - var - iphlpapi : thandle; - getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall; -{$endif} + procedure populatednsserverlist; var - {$ifdef win32} - fixed_info : pfixed_info; - fixed_info_len : longint; - currentdnsserver : pip_addr_string; - {$else} - t:textfile; - s:string; - a:integer; - {$endif} + a:integer; begin - //result := ''; - if assigned(dnsserverlist) then begin - dnsserverlist.clear; + if assigned(dnsserverlag) then begin + dnsserverlag.clear; end else begin - dnsserverlist := tstringlist.Create; + dnsserverlag := tlist.Create; end; - {$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); - getmem(fixed_info,fixed_info_len); - if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin - freemem(fixed_info); - exit; - end; - currentdnsserver := @(fixed_info.dnsserverlist); - while assigned(currentdnsserver) do begin - dnsserverlist.Add(currentdnsserver.IpAddress); - currentdnsserver := currentdnsserver.next; - end; - freemem(fixed_info); - {$else} - filemode := 0; - assignfile(t,'/etc/resolv.conf'); - {$i-}reset(t);{$i+} - if ioresult <> 0 then exit; - - while not eof(t) do begin - readln(t,s); - if not (copy(s,1,10) = 'nameserver') then continue; - s := copy(s,11,500); - while s <> '' do begin - if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break; - end; - a := pos(' ',s); - if a <> 0 then s := copy(s,1,a-1); - a := pos(#9,s); - if a <> 0 then s := copy(s,1,a-1); - //result := s; - //if result <> '' then break; - dnsserverlist.Add(s); - end; - close(t); - {$endif} + + dnsserverlist := getsystemdnsservers; + for a := biniplist_getcount(dnsserverlist)-1 downto 0 do dnsserverlag.Add(nil); end; procedure cleardnsservercache; begin - if assigned(dnsserverlist) then begin - dnsserverlist.destroy; - dnsserverlist := nil; + if assigned(dnsserverlag) then begin + dnsserverlag.destroy; + dnsserverlag := nil; + dnsserverlist := ''; end; end; -function getcurrentsystemnameserver(var id:integer):string; +function getcurrentsystemnameserverbin(var id:integer):tbinip; var counter : integer; - begin - if not assigned(dnsserverlist) then populatednsserverlist; - if dnsserverlist.count=0 then raise exception.create('no dns servers availible'); - id := 0; - if dnsserverlist.count >1 then begin + {override the name server choice here, instead of overriding it wherever it's called + setting ID to -1 causes it to be ignored in reportlag} + if (overridednsserver <> '') then begin + result := ipstrtobinf(overridednsserver); + if result.family <> 0 then begin + id := -1; + exit; + end; + end; - for counter := 1 to dnsserverlist.count-1 do begin - if taddrint(dnsserverlist.objects[counter]) < taddrint(dnsserverlist.objects[id]) then id := counter; + if not assigned(dnsserverlag) then populatednsserverlist; + if dnsserverlag.count=0 then raise exception.create('no dns servers available'); + id := 0; + if dnsserverlag.count >1 then begin + for counter := dnsserverlag.count-1 downto 1 do begin + if taddrint(dnsserverlag[counter]) < taddrint(dnsserverlag[id]) then id := counter; end; end; - result := dnsserverlist[id] + result := biniplist_get(dnsserverlist,id); +end; + +function getcurrentsystemnameserver(var id:integer):ansistring; +begin + result := ipbintostr(getcurrentsystemnameserverbin(id)); end; procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout @@ -689,44 +670,18 @@ var counter : integer; temp : integer; begin - if (id < 0) or (id >= dnsserverlist.count) then exit; + if (id < 0) or (id >= dnsserverlag.count) then exit; if lag = -1 then lag := timeoutlag; - for counter := 0 to dnsserverlist.count-1 do begin - temp := taddrint(dnsserverlist.objects[counter]) *15; + for counter := 0 to dnsserverlag.count-1 do begin + temp := taddrint(dnsserverlag[counter]) *15; if counter=id then temp := temp + lag; - dnsserverlist.objects[counter] := tobject(temp div 16); + dnsserverlag[counter] := tobject(temp div 16); end; end; - -{$ifdef linux}{$ifdef ipv6} -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; +{$ifdef ipv6} procedure initpreferredmode; var @@ -738,8 +693,9 @@ var begin if preferredmodeinited then exit; if useaf <> useaf_default then exit; - useaf := useaf_preferv4; l := getv6localips; + if biniplist_getcount(l) = 0 then exit; + useaf := useaf_preferv4; ipstrtobin('2000::',ipmask_global); ipstrtobin('2001::',ipmask_teredo); ipstrtobin('2002::',ipmask_6to4); @@ -755,7 +711,7 @@ begin end; end; -{$endif}{$endif} +{$endif} { quick and dirty description of dns packet structure to aid writing and