1 { Copyright (C) 2005 Bas Steendijk and Peter Green
\r
2 For conditions of distribution and use, see copyright notice in zlib_license.txt
\r
3 which is included in the package
\r
4 ----------------------------------------------------------------------------- }
\r
8 code wanting to use this dns system should act as follows (note: app
\r
9 developers will probablly want to use dnsasync or dnssync or write a similar
\r
10 wrapper unit of thier own).
\r
12 for normal lookups call setstate_forward or setstate_reverse to set up the
\r
13 state, for more obscure lookups use setstate_request_init and fill in other
\r
14 relavent state manually.
\r
16 call state_process which will do processing on the information in the state
\r
17 and return an action
\r
18 action_ignore means that dnscore wants the code that calls it to go
\r
19 back to waiting for packets
\r
20 action_sendpacket means that dnscore wants the code that calls it to send
\r
21 the packet in sendpacket/sendpacketlen and then start (or go back to) listening
\r
23 action_done means the request has completed (either suceeded or failed)
\r
25 callers should resend the last packet they tried to send if they have not
\r
26 been asked to send a new packet for more than some timeout value they choose.
\r
28 when a packet is received the application should put the packet in
\r
29 recvbuf/recvbuflen , set state.parsepacket and call state_process again
\r
31 once the app gets action_done it can determine success or failure in the
\r
34 on failure state.resultstr will be an empty string and state.resultbin will
\r
35 be zeroed out (easilly detected by the fact that it will have a family of 0)
\r
37 on success for a A or AAAA lookup state.resultstr will be an empty string
\r
38 and state.resultbin will contain the result (note: AAAA lookups require IPV6
\r
41 if an A lookup fails and the code is built with ipv6 enabled then the code
\r
42 will return any AAAA records with the same name. The reverse does not apply
\r
43 so if an application preffers IPV6 but wants IPV4 results as well it must
\r
44 check them seperately.
\r
46 on success for any other type of lookup state.resultstr will be an empty
\r
48 note the state contains ansistrings, setstate_init with a null name parameter
\r
49 can be used to clean theese up if required.
\r
51 callers may use setstate_failure to mark the state as failed themseleves
\r
52 before passing it on to other code, for example this may be done in the event
\r
57 {$ifdef fpc}{$mode delphi}{$endif}
\r
59 {$include lcoreconfig.inc}
\r
63 uses binipstuff,classes,pgtypes,lcorernd;
\r
65 var usewindns : boolean = {$ifdef win32}true{$else}false{$endif};
\r
66 {hint to users of this unit that they should use windows dns instead.
\r
67 May be disabled by applications if desired. (e.g. if setting a custom
\r
70 note: this unit will not be able to self populate it's dns server list on
\r
71 older versions of windows.}
\r
80 hint to users of this unit to how to deal with connecting to hostnames regarding ipv4 or ipv6 usage
\r
81 can be set by apps as desired
\r
83 var useaf:integer = useaf_default;
\r
86 (temporarily) use a different nameserver, regardless of the dnsserverlist
\r
88 var overridednsserver:ansistring;
\r
93 //note: when using action_ignore the dnscore code *must* preserve the contents of state.sendpacket to allow for retries
\r
94 //note: action_ignore must not be used in response to the original request but there is no valid reason for doing this anyway
\r
111 retryafter=300000; //microseconds must be less than one second;
\r
112 timeoutlag=1000000000; // penalty value to be treated as lag in the event of a timeout (microseconds)
\r
114 dvar=array[0..0] of byte;
\r
116 tdnspacket=packed record
\r
119 rrcount:array[0..3] of word;
\r
120 payload:array[0..511-12] of byte;
\r
127 recursioncount:integer;
\r
128 queryname:ansistring;
\r
130 parsepacket:boolean;
\r
131 resultstr:ansistring;
\r
133 resultlist:tbiniplist;
\r
134 resultaction:integer;
\r
135 numrr1:array[0..3] of integer;
\r
138 sendpacketlen:integer;
\r
139 sendpacket:tdnspacket;
\r
140 recvpacketlen:integer;
\r
141 recvpacket:tdnspacket;
\r
142 forwardfamily:integer;
\r
146 requesttypehi:byte;
\r
151 data:array[0..511] of byte;
\r
154 trrpointer=packed record
\r
161 //commenting out functions from interface that do not have documented semantics
\r
162 //and probablly should not be called from outside this unit, reenable them
\r
163 //if you must but please document them at the same time --plugwash
\r
165 //function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer;
\r
167 //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
\r
168 function makereversename(const binip:tbinip):ansistring;
\r
170 procedure setstate_request_init(const name:ansistring;var state:tdnsstate);
\r
172 //set up state for a foward lookup. A family value of AF_INET6 will give only
\r
173 //ipv6 results. Any other value will give only ipv4 results
\r
174 procedure setstate_forward(const name:ansistring;var state:tdnsstate;family:integer);
\r
176 procedure setstate_reverse(const binip:tbinip;var state:tdnsstate);
\r
177 procedure setstate_failure(var state:tdnsstate);
\r
178 //procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate);
\r
180 //for custom raw lookups such as TXT, as desired by the user
\r
181 procedure setstate_custom(const name:ansistring; requesttype:integer; var state:tdnsstate);
\r
183 procedure state_process(var state:tdnsstate);
\r
185 //function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):string;
\r
187 procedure populatednsserverlist;
\r
188 procedure cleardnsservercache;
\r
191 dnsserverlist : tstringlist;
\r
192 // currentdnsserverno : integer;
\r
195 //getcurrentsystemnameserver returns the nameserver the app should use and sets
\r
196 //id to the id of that nameserver. id should later be used to report how laggy
\r
197 //the servers response was and if it was timed out.
\r
198 function getcurrentsystemnameserver(var id:integer) :ansistring;
\r
199 procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout
\r
202 // unixnameservercache:string;
\r
207 function getv6localips:tbiniplist;
\r
208 procedure initpreferredmode;
\r
211 preferredmodeinited:boolean;
\r
216 failurereason:ansistring;
\r
218 function getquerytype(s:ansistring):integer;
\r
231 function getquerytype(s:ansistring):integer;
\r
235 if (s = 'A') then result := querytype_a else
\r
236 if (s = 'CNAME') then result := querytype_cname else
\r
237 if (s = 'AAAA') then result := querytype_aaaa else
\r
238 if (s = 'PTR') then result := querytype_ptr else
\r
239 if (s = 'NS') then result := querytype_ns else
\r
240 if (s = 'MX') then result := querytype_mx else
\r
241 if (s = 'A6') then result := querytype_a6 else
\r
242 if (s = 'TXT') then result := querytype_txt else
\r
243 if (s = 'SOA') then result := querytype_soa else
\r
244 if (s = 'SPF') then result := querytype_spf;
\r
247 function buildrequest(const name:ansistring;var packet:tdnspacket;requesttype:word):integer;
\r
251 arr:array[0..sizeof(packet)-1] of byte absolute packet;
\r
253 { writeln('buildrequest: name: ',name);}
\r
255 fillchar(packet,sizeof(packet),0);
\r
256 packet.id := randominteger($10000);
\r
258 packet.flags := htons($0100);
\r
259 packet.rrcount[0] := htons($0001);
\r
262 s := copy(name,1,maxnamelength);
\r
263 if s = '' then exit;
\r
264 if s[length(s)] <> '.' then s := s + '.';
\r
267 if (s = '.') then begin
\r
268 packet.payload[0] := 0;
\r
271 for a := 1 to length(s) do begin
\r
272 if s[a] = '.' then begin
\r
273 if b > maxnamefieldlen then exit;
\r
274 if (b = 0) then exit;
\r
275 packet.payload[a-b-1] := b;
\r
278 packet.payload[a] := byte(s[a]);
\r
282 if b > maxnamefieldlen then exit;
\r
283 packet.payload[length(s)-b] := b;
\r
284 result := length(s) + 12+5;
\r
287 arr[result-1] := 1;
\r
288 arr[result-3] := requesttype and $ff;
\r
289 arr[result-4] := requesttype shr 8;
\r
292 function makereversename(const binip:tbinip):ansistring;
\r
298 if binip.family = AF_INET then begin
\r
299 b := htonl(binip.ip);
\r
300 for a := 0 to 3 do begin
\r
301 name := name + inttostr(b shr (a shl 3) and $ff)+'.';
\r
303 name := name + 'in-addr.arpa';
\r
306 if binip.family = AF_INET6 then begin
\r
307 for a := 15 downto 0 do begin
\r
308 b := binip.ip6.u6_addr8[a];
\r
309 name := name + hexchars[b and $f]+'.'+hexchars[b shr 4]+'.';
\r
311 name := name + 'ip6.arpa';
\r
321 decodes DNS format name to a string. does not includes the root dot.
\r
322 doesnt read beyond len.
\r
323 empty result + non null failurereason: failure
\r
324 empty result + null failurereason: internal use
\r
326 function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):ansistring;
\r
328 arr:array[0..sizeof(packet)-1] of byte absolute packet;
\r
334 if (start+numread < 0) or (start+numread >= len) then begin
\r
336 failurereason := 'decoding name: got out of range1';
\r
339 b := arr[start+numread];
\r
340 if b >= $c0 then begin
\r
341 {recursive sub call}
\r
342 if recursion > 10 then begin
\r
344 failurereason := 'decoding name: max recursion';
\r
347 if ((start+numread+1) >= len) then begin
\r
349 failurereason := 'decoding name: got out of range3';
\r
352 a := ((b shl 8) or arr[start+numread+1]) and $3fff;
\r
353 s := decodename(packet,len,a,recursion+1,a);
\r
354 if (s = '') and (failurereason <> '') then begin
\r
358 if result <> '' then result := result + '.';
\r
359 result := result + s;
\r
362 end else if b < 64 then begin
\r
363 if (numread <> 0) and (b <> 0) then result := result + '.';
\r
364 for a := start+numread+1 to start+numread+b do begin
\r
365 if (a >= len) then begin
\r
367 failurereason := 'decoding name: got out of range2';
\r
370 result := result + ansichar(arr[a]);
\r
374 if b = 0 then begin
\r
375 if (result = '') and (recursion = 0) then result := '.';
\r
376 exit; {reached end of name}
\r
379 failurereason := 'decoding name: read invalid char';
\r
386 {==============================================================================}
\r
388 function getrawfromrr(const rrp:trrpointer;len:integer):ansistring;
\r
390 setlength(result,htons(trr(rrp.p^).datalen));
\r
391 uniquestring(result);
\r
392 move(trr(rrp.p^).data,result[1],length(result));
\r
396 function getipfromrr(const rrp:trrpointer;len:integer):tbinip;
\r
398 fillchar(result,sizeof(result),0);
\r
399 case trr(rrp.p^).requesttype of
\r
401 if htons(trr(rrp.p^).datalen) <> 4 then exit;
\r
402 move(trr(rrp.p^).data,result.ip,4);
\r
403 result.family :=AF_INET;
\r
406 querytype_aaaa: begin
\r
407 if htons(trr(rrp.p^).datalen) <> 16 then exit;
\r
408 result.family := AF_INET6;
\r
409 move(trr(rrp.p^).data,result.ip6,16);
\r
417 procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate);
\r
421 state.resultaction := action_done;
\r
422 state.resultstr := '';
\r
423 case trr(rrp.p^).requesttype of
\r
424 querytype_a{$ifdef ipv6},querytype_aaaa{$endif}: begin
\r
425 state.resultbin := getipfromrr(rrp,len);
\r
427 querytype_txt:begin
\r
428 {TXT returns a raw string}
\r
429 state.resultstr := copy(getrawfromrr(rrp,len),2,9999);
\r
430 fillchar(state.resultbin,sizeof(state.resultbin),0);
\r
433 {MX is a name after a 16 bits word}
\r
434 state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+12,0,a);
\r
435 fillchar(state.resultbin,sizeof(state.resultbin),0);
\r
438 {other reply types (PTR, MX) return a hostname}
\r
439 state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+10,0,a);
\r
440 fillchar(state.resultbin,sizeof(state.resultbin),0);
\r
444 procedure setstate_request_init(const name:ansistring;var state:tdnsstate);
\r
446 {destroy things properly}
\r
447 state.resultstr := '';
\r
448 state.queryname := '';
\r
449 state.rrdata := '';
\r
450 fillchar(state,sizeof(state),0);
\r
451 state.queryname := name;
\r
452 state.parsepacket := false;
\r
455 procedure setstate_forward(const name:ansistring;var state:tdnsstate;family:integer);
\r
457 setstate_request_init(name,state);
\r
458 state.forwardfamily := family;
\r
460 if family = AF_INET6 then state.requesttype := querytype_aaaa else
\r
462 state.requesttype := querytype_a;
\r
465 procedure setstate_reverse(const binip:tbinip;var state:tdnsstate);
\r
467 setstate_request_init(makereversename(binip),state);
\r
468 state.requesttype := querytype_ptr;
\r
471 procedure setstate_custom(const name:ansistring; requesttype:integer; var state:tdnsstate);
\r
473 setstate_request_init(name,state);
\r
474 state.requesttype := requesttype;
\r
478 procedure setstate_failure(var state:tdnsstate);
\r
480 state.resultstr := '';
\r
481 fillchar(state.resultbin,sizeof(state.resultbin),0);
\r
482 state.resultaction := action_done;
\r
485 procedure state_process(var state:tdnsstate);
\r
491 rrptemp:^trrpointer;
\r
493 if state.parsepacket then begin
\r
494 if state.recvpacketlen < 12 then begin
\r
495 failurereason := 'Undersized packet';
\r
496 state.resultaction := action_ignore;
\r
499 if state.id <> state.recvpacket.id then begin
\r
500 failurereason := 'ID mismatch';
\r
501 state.resultaction := action_ignore;
\r
505 for a := 0 to 3 do begin
\r
506 state.numrr1[a] := htons(state.recvpacket.rrcount[a]);
\r
507 if state.numrr1[a] > maxrrofakind then goto failure;
\r
508 inc(state.numrr2,state.numrr1[a]);
\r
511 setlength(state.rrdata,state.numrr2*sizeof(trrpointer));
\r
513 {- put all replies into a list}
\r
517 for a := 0 to state.numrr1[0]-1 do begin
\r
518 if (ofs < 12) or (ofs > state.recvpacketlen-4) then goto failure;
\r
519 rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
\r
520 rrptemp.p := @state.recvpacket.payload[ofs-12];
\r
521 rrptemp.ofs := ofs;
\r
522 decodename(state.recvpacket,state.recvpacketlen,ofs,0,b);
\r
523 rrptemp.len := b + 4;
\r
524 inc(ofs,rrptemp.len);
\r
527 for a := state.numrr1[0] to state.numrr2-1 do begin
\r
528 if (ofs < 12) or (ofs > state.recvpacketlen-12) then goto failure;
\r
529 rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
\r
530 if decodename(state.recvpacket,state.recvpacketlen,ofs,0,b) = '' then goto failure;
\r
531 rrtemp := @state.recvpacket.payload[ofs-12+b]; {rrtemp points to values and result, after initial name}
\r
532 rrptemp.p := rrtemp;
\r
533 rrptemp.ofs := ofs; {ofs is start of RR before initial name from start of packet}
\r
534 rrptemp.namelen := b;
\r
535 b := htons(rrtemp.datalen);
\r
536 rrptemp.len := b + 10 + rrptemp.namelen;
\r
537 inc(ofs,rrptemp.len);
\r
539 if (ofs <> state.recvpacketlen) then begin
\r
540 failurereason := 'ofs <> state.packetlen';
\r
544 {if we requested A or AAAA build a list of all replies}
\r
545 if (state.requesttype = querytype_a) or (state.requesttype = querytype_aaaa) then begin
\r
546 state.resultlist := biniplist_new;
\r
547 for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin
\r
548 rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
\r
549 rrtemp := rrptemp.p;
\r
551 if rrtemp.requesttype = state.requesttype then begin
\r
552 biniplist_add(state.resultlist,getipfromrr(rrptemp^,b));
\r
557 {- check for items of the requested type in answer section, if so return success first}
\r
558 for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin
\r
559 rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
\r
560 rrtemp := rrptemp.p;
\r
562 if rrtemp.requesttype = state.requesttype then begin
\r
563 setstate_return(rrptemp^,b,state);
\r
568 {if no items of correct type found, follow first cname in answer section}
\r
569 for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin
\r
570 rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
\r
571 rrtemp := rrptemp.p;
\r
573 if rrtemp.requesttype = querytype_cname then begin
\r
574 state.queryname := decodename(state.recvpacket,state.recvpacketlen,rrptemp.ofs+12,0,b);
\r
579 {no cnames found, no items of correct type found}
\r
580 if state.forwardfamily <> 0 then goto failure;
\r
584 {here it needs recursed lookup}
\r
585 {if needing to follow a cname, change state to do so}
\r
586 inc(state.recursioncount);
\r
587 if state.recursioncount > maxrecursion then goto failure;
\r
590 {here, a name needs to be resolved}
\r
591 if state.queryname = '' then begin
\r
592 failurereason := 'empty query name';
\r
596 {do /ets/hosts lookup here}
\r
597 state.sendpacketlen := buildrequest(state.queryname,state.sendpacket,state.requesttype);
\r
598 if state.sendpacketlen = 0 then begin
\r
599 failurereason := 'building request packet failed';
\r
602 state.id := state.sendpacket.id;
\r
603 state.resultaction := action_sendquery;
\r
607 setstate_failure(state);
\r
611 MAX_HOSTNAME_LEN = 132;
\r
612 MAX_DOMAIN_NAME_LEN = 132;
\r
613 MAX_SCOPE_ID_LEN = 260 ;
\r
614 MAX_ADAPTER_NAME_LENGTH = 260;
\r
615 MAX_ADAPTER_ADDRESS_LENGTH = 8;
\r
616 MAX_ADAPTER_DESCRIPTION_LENGTH = 132;
\r
617 ERROR_BUFFER_OVERFLOW = 111;
\r
618 MIB_IF_TYPE_ETHERNET = 6;
\r
619 MIB_IF_TYPE_TOKENRING = 9;
\r
620 MIB_IF_TYPE_FDDI = 15;
\r
621 MIB_IF_TYPE_PPP = 23;
\r
622 MIB_IF_TYPE_LOOPBACK = 24;
\r
623 MIB_IF_TYPE_SLIP = 28;
\r
627 tip_addr_string=packed record
\r
629 IpAddress : array[0..15] of ansichar;
\r
630 ipmask : array[0..15] of ansichar;
\r
633 pip_addr_string=^tip_addr_string;
\r
634 tFIXED_INFO=packed record
\r
635 HostName : array[0..MAX_HOSTNAME_LEN-1] of ansichar;
\r
636 DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of ansichar;
\r
637 currentdnsserver : pip_addr_string;
\r
638 dnsserverlist : tip_addr_string;
\r
639 nodetype : longint;
\r
640 ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of ansichar;
\r
641 enablerouting : longbool;
\r
642 enableproxy : longbool;
\r
643 enabledns : longbool;
\r
645 pFIXED_INFO=^tFIXED_INFO;
\r
648 iphlpapi : thandle;
\r
649 getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall;
\r
651 procedure populatednsserverlist;
\r
654 fixed_info : pfixed_info;
\r
655 fixed_info_len : longint;
\r
656 currentdnsserver : pip_addr_string;
\r
664 if assigned(dnsserverlist) then begin
\r
665 dnsserverlist.clear;
\r
667 dnsserverlist := tstringlist.Create;
\r
670 if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll');
\r
671 if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams');
\r
672 if not assigned(getnetworkparams) then exit;
\r
673 fixed_info_len := 0;
\r
674 if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit;
\r
675 //fixed_info_len :=sizeof(tfixed_info);
\r
676 getmem(fixed_info,fixed_info_len);
\r
677 if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin
\r
678 freemem(fixed_info);
\r
681 currentdnsserver := @(fixed_info.dnsserverlist);
\r
682 while assigned(currentdnsserver) do begin
\r
683 dnsserverlist.Add(currentdnsserver.IpAddress);
\r
684 currentdnsserver := currentdnsserver.next;
\r
686 freemem(fixed_info);
\r
689 assignfile(t,'/etc/resolv.conf');
\r
690 {$i-}reset(t);{$i+}
\r
691 if ioresult <> 0 then exit;
\r
693 while not eof(t) do begin
\r
695 if not (copy(s,1,10) = 'nameserver') then continue;
\r
696 s := copy(s,11,500);
\r
697 while s <> '' do begin
\r
698 if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break;
\r
701 if a <> 0 then s := copy(s,1,a-1);
\r
703 if a <> 0 then s := copy(s,1,a-1);
\r
705 //if result <> '' then break;
\r
706 dnsserverlist.Add(s);
\r
712 procedure cleardnsservercache;
\r
714 if assigned(dnsserverlist) then begin
\r
715 dnsserverlist.destroy;
\r
716 dnsserverlist := nil;
\r
720 function getcurrentsystemnameserver(var id:integer):ansistring;
\r
725 if not assigned(dnsserverlist) then populatednsserverlist;
\r
726 if dnsserverlist.count=0 then raise exception.create('no dns servers availible');
\r
728 if dnsserverlist.count >1 then begin
\r
730 for counter := 1 to dnsserverlist.count-1 do begin
\r
731 if taddrint(dnsserverlist.objects[counter]) < taddrint(dnsserverlist.objects[id]) then id := counter;
\r
734 result := dnsserverlist[id]
\r
737 procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout
\r
742 if (id < 0) or (id >= dnsserverlist.count) then exit;
\r
743 if lag = -1 then lag := timeoutlag;
\r
744 for counter := 0 to dnsserverlist.count-1 do begin
\r
745 temp := taddrint(dnsserverlist.objects[counter]) *15;
\r
746 if counter=id then temp := temp + lag;
\r
747 dnsserverlist.objects[counter] := tobject(temp div 16);
\r
757 function getv6localips:tbiniplist;
\r
764 result := biniplist_new;
\r
766 assignfile(t,'/proc/net/if_inet6');
\r
767 {$i-}reset(t);{$i+}
\r
768 if ioresult <> 0 then exit; {none found, return empty list}
\r
770 while not eof(t) do begin
\r
773 for a := 0 to 7 do begin
\r
774 if (s2 <> '') then s2 := s2 + ':';
\r
775 s2 := s2 + copy(s,(a shl 2)+1,4);
\r
778 if ip.family <> 0 then biniplist_add(result,ip);
\r
785 {the following code's purpose is to determine what IP windows would come from, to reach an IP
\r
786 it can be abused to find if there's any global v6 IPs, getaddrinfo seems unreliable (not working on XP atleast)
\r
789 SIO_ROUTING_INTERFACE_QUERY = $c8000014;
\r
790 function WSAIoctl(s: TSocket; code:integer; const Buf; len: Integer; var output; outlen:integer; var outreturned: Integer; overlapped:pointer; completion: pointer): Integer; stdcall; external 'ws2_32.dll' name 'WSAIoctl';
\r
792 function getlocalipforip(const ip:tbinip):tbinip;
\r
796 inaddrv,inaddrv2:tinetsockaddrv;
\r
797 srcx:winsock.tsockaddr absolute inaddrv2;
\r
799 makeinaddrv(ip,'0',inaddrv);
\r
800 handle := Socket(inaddrv.inaddr.family,SOCK_DGRAM,IPPROTO_UDP);
\r
801 if WSAIoctl(handle, SIO_ROUTING_INTERFACE_QUERY, inaddrv, sizeof(inaddrv), inaddrv2, sizeof(inaddrv2), a, nil, nil) <> 0
\r
802 then raise exception.create('getlocalipforip failed with error: '+inttostr(wsagetlasterror));
\r
803 result := inaddrvtobinip(inaddrv2);
\r
804 closesocket(handle);
\r
807 function getv6localips:tbiniplist;
\r
809 result := biniplist_new;
\r
810 {this IP is chosen because it's the first normal global v6 IP that has no special purpose}
\r
811 biniplist_add(result,getlocalipforip(ipstrtobinf('2001:200::')));
\r
815 procedure initpreferredmode;
\r
820 ipmask_global,ipmask_6to4,ipmask_teredo:tbinip;
\r
823 if preferredmodeinited then exit;
\r
824 if useaf <> useaf_default then exit;
\r
825 l := getv6localips;
\r
826 if biniplist_getcount(l) = 0 then exit;
\r
827 useaf := useaf_preferv4;
\r
828 ipstrtobin('2000::',ipmask_global);
\r
829 ipstrtobin('2001::',ipmask_teredo);
\r
830 ipstrtobin('2002::',ipmask_6to4);
\r
831 {if there is any v6 IP which is globally routable and not 6to4 and not teredo, prefer v6}
\r
832 for a := biniplist_getcount(l)-1 downto 0 do begin
\r
833 ip := biniplist_get(l,a);
\r
834 if not comparebinipmask(ip,ipmask_global,3) then continue;
\r
835 if comparebinipmask(ip,ipmask_teredo,32) then continue;
\r
836 if comparebinipmask(ip,ipmask_6to4,16) then continue;
\r
837 useaf := useaf_preferv6;
\r
838 preferredmodeinited := true;
\r
846 { quick and dirty description of dns packet structure to aid writing and
\r
847 understanding of parser code, refer to appropriate RFCs for proper specs
\r
848 - all words are network order
\r
850 www.google.com A request:
\r
852 0, 2: random transaction ID
\r
853 2, 2: flags: only the "recursion desired" bit set. (bit 8 of word)
\r
855 6, 2: answer RR's: 0.
\r
856 8, 2: authority RR's: 0.
\r
857 10, 2: additional RR's: 0.
\r
860 #03 "www" #06 "google" #03 "com" #00
\r
861 size-4, 2: type: host address (1)
\r
862 size-2, 2: class: inet (1)
\r
866 0,2: random transaction ID
\r
867 2,2: flags: set: response (bit 15), recursion desired (8), recursion available (7)
\r
869 6,4: answer RR's: 2
\r
870 8,4: authority RR's: 9
\r
871 10,4: additional RR's: 9
\r
876 0,2 "c0 0c" "name: www.google.com"
\r
877 2,2 "00 05" "type: cname for an alias"
\r
878 4,2 "00 01" "class: inet"
\r
880 10,2: data length "00 17" (23)
\r
881 12: the cname name (www.google.akadns.net)
\r
884 2,2 "00 01" host address
\r
887 10,2: data length (4)
\r
889 authority - 9 records
\r
890 additional - 9 records
\r
896 4,2: class: inet (0001)
\r
898 10,2: data size (16)
\r
901 ptr request: query type 000c
\r
903 name compression: word "cxxx" in the name, xxx points to offset in the packet}
\r