You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dnscore.pas 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. { Copyright (C) 2005 Bas Steendijk and Peter Green
  2. For conditions of distribution and use, see copyright notice in zlib_license.txt
  3. which is included in the package
  4. ----------------------------------------------------------------------------- }
  5. {
  6. code wanting to use this dns system should act as follows (note: app
  7. developers will probablly want to use dnsasync or dnssync or write a similar
  8. wrapper unit of thier own).
  9. for normal lookups call setstate_forward or setstate_reverse to set up the
  10. state, for more obscure lookups use setstate_request_init and fill in other
  11. relavent state manually.
  12. call state_process which will do processing on the information in the state
  13. and return an action
  14. action_ignore means that dnscore wants the code that calls it to go
  15. back to waiting for packets
  16. action_sendpacket means that dnscore wants the code that calls it to send
  17. the packet in sendpacket/sendpacketlen and then start (or go back to) listening
  18. for
  19. action_done means the request has completed (either suceeded or failed)
  20. callers should resend the last packet they tried to send if they have not
  21. been asked to send a new packet for more than some timeout value they choose.
  22. when a packet is received the application should put the packet in
  23. recvbuf/recvbuflen , set state.parsepacket and call state_process again
  24. once the app gets action_done it can determine sucess or failure in the
  25. following ways.
  26. on failure state.resultstr will be an empty string and state.resultbin will
  27. be zeroed out (easilly detected by the fact that it will have a family of 0)
  28. on success for a A or AAAA lookup state.resultstr will be an empty string
  29. and state.resultbin will contain the result (note: AAAA lookups require IPV6
  30. enabled).
  31. if an A lookup fails and the code is built with ipv6 enabled then the code
  32. will return any AAAA records with the same name. The reverse does not apply
  33. so if an application preffers IPV6 but wants IPV4 results as well it must
  34. check them seperately.
  35. on success for any other type of lookup state.resultstr will be an empty
  36. note the state contains ansistrings, setstate_init with a null name parameter
  37. can be used to clean theese up if required.
  38. callers may use setstate_failure to mark the state as failed themseleves
  39. before passing it on to other code, for example this may be done in the event
  40. of a timeout.
  41. }
  42. unit dnscore;
  43. {$ifdef fpc}{$mode delphi}{$endif}
  44. {$include lcoreconfig.inc}
  45. interface
  46. uses binipstuff,classes,pgtypes,lcorernd;
  47. var usewindns : boolean = {$ifdef win32}true{$else}false{$endif};
  48. {hint to users of this unit that they should use windows dns instead.
  49. May be disabled by applications if desired. (e.g. if setting a custom
  50. dnsserverlist).
  51. note: this unit will not be able to self populate it's dns server list on
  52. older versions of windows.}
  53. const
  54. useaf_default=0;
  55. useaf_preferv4=1;
  56. useaf_preferv6=2;
  57. useaf_v4=3;
  58. useaf_v6=4;
  59. {
  60. hint to users of this unit to how to deal with connecting to hostnames regarding ipv4 or ipv6 usage
  61. can be set by apps as desired
  62. }
  63. var useaf:integer = useaf_default;
  64. {
  65. (temporarily) use a different nameserver, regardless of the dnsserverlist
  66. }
  67. var overridednsserver:string;
  68. const
  69. maxnamelength=127;
  70. maxnamefieldlen=63;
  71. //note: when using action_ignore the dnscore code *must* preserve the contents of state.sendpacket to allow for retries
  72. //note: action_ignore must not be used in response to the original request but there is no valid reason for doing this anyway
  73. action_ignore=0;
  74. action_done=1;
  75. action_sendquery=2;
  76. querytype_a=1;
  77. querytype_cname=5;
  78. querytype_aaaa=28;
  79. querytype_a6=38;
  80. querytype_ptr=12;
  81. querytype_ns=2;
  82. querytype_soa=6;
  83. querytype_mx=15;
  84. querytype_txt=16;
  85. querytype_spf=99;
  86. maxrecursion=50;
  87. maxrrofakind=20;
  88. retryafter=300000; //microseconds must be less than one second;
  89. timeoutlag=1000000000; // penalty value to be treated as lag in the event of a timeout (microseconds)
  90. type
  91. dvar=array[0..0] of byte;
  92. pdvar=^dvar;
  93. tdnspacket=packed record
  94. id:word;
  95. flags:word;
  96. rrcount:array[0..3] of word;
  97. payload:array[0..511-12] of byte;
  98. end;
  99. tdnsstate=record
  100. id:word;
  101. recursioncount:integer;
  102. queryname:string;
  103. requesttype:word;
  104. parsepacket:boolean;
  105. resultstr:string;
  106. resultbin:tbinip;
  107. resultlist:tbiniplist;
  108. resultaction:integer;
  109. numrr1:array[0..3] of integer;
  110. numrr2:integer;
  111. rrdata:string;
  112. sendpacketlen:integer;
  113. sendpacket:tdnspacket;
  114. recvpacketlen:integer;
  115. recvpacket:tdnspacket;
  116. forwardfamily:integer;
  117. end;
  118. trr=packed record
  119. requesttypehi:byte;
  120. requesttype:byte;
  121. clas:word;
  122. ttl:integer;
  123. datalen:word;
  124. data:array[0..511] of byte;
  125. end;
  126. trrpointer=packed record
  127. p:pointer;
  128. ofs:integer;
  129. len:integer;
  130. namelen:integer;
  131. end;
  132. //commenting out functions from interface that do not have documented semantics
  133. //and probablly should not be called from outside this unit, reenable them
  134. //if you must but please document them at the same time --plugwash
  135. //function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer;
  136. //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
  137. function makereversename(const binip:tbinip):string;
  138. procedure setstate_request_init(const name:string;var state:tdnsstate);
  139. //set up state for a foward lookup. A family value of AF_INET6 will give only
  140. //ipv6 results. Any other value will give only ipv4 results
  141. procedure setstate_forward(const name:string;var state:tdnsstate;family:integer);
  142. procedure setstate_reverse(const binip:tbinip;var state:tdnsstate);
  143. procedure setstate_failure(var state:tdnsstate);
  144. //procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate);
  145. //for custom raw lookups such as TXT, as desired by the user
  146. procedure setstate_custom(const name:string; requesttype:integer; var state:tdnsstate);
  147. procedure state_process(var state:tdnsstate);
  148. //function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):string;
  149. procedure populatednsserverlist;
  150. procedure cleardnsservercache;
  151. var
  152. dnsserverlist : tstringlist;
  153. // currentdnsserverno : integer;
  154. //getcurrentsystemnameserver returns the nameserver the app should use and sets
  155. //id to the id of that nameserver. id should later be used to report how laggy
  156. //the servers response was and if it was timed out.
  157. function getcurrentsystemnameserver(var id:integer) :string;
  158. procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout
  159. //var
  160. // unixnameservercache:string;
  161. { $endif}
  162. {$ifdef ipv6}
  163. function getv6localips:tbiniplist;
  164. procedure initpreferredmode;
  165. var
  166. preferredmodeinited:boolean;
  167. {$endif}
  168. var
  169. failurereason:string;
  170. function getquerytype(s:string):integer;
  171. implementation
  172. uses
  173. {$ifdef win32}
  174. windows,
  175. {$endif}
  176. sysutils;
  177. function getquerytype(s:string):integer;
  178. begin
  179. s := uppercase(s);
  180. result := 0;
  181. if (s = 'A') then result := querytype_a else
  182. if (s = 'CNAME') then result := querytype_cname else
  183. if (s = 'AAAA') then result := querytype_aaaa else
  184. if (s = 'PTR') then result := querytype_ptr else
  185. if (s = 'NS') then result := querytype_ns else
  186. if (s = 'MX') then result := querytype_mx else
  187. if (s = 'A6') then result := querytype_a6 else
  188. if (s = 'TXT') then result := querytype_txt else
  189. if (s = 'SOA') then result := querytype_soa else
  190. if (s = 'SPF') then result := querytype_spf;
  191. end;
  192. function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer;
  193. var
  194. a,b:integer;
  195. s:string;
  196. arr:array[0..sizeof(packet)-1] of byte absolute packet;
  197. begin
  198. { writeln('buildrequest: name: ',name);}
  199. result := 0;
  200. fillchar(packet,sizeof(packet),0);
  201. packet.id := randominteger($10000);
  202. packet.flags := htons($0100);
  203. packet.rrcount[0] := htons($0001);
  204. s := copy(name,1,maxnamelength);
  205. if s = '' then exit;
  206. if s[length(s)] <> '.' then s := s + '.';
  207. b := 0;
  208. {encode name}
  209. if (s = '.') then begin
  210. packet.payload[0] := 0;
  211. result := 12+5;
  212. end else begin
  213. for a := 1 to length(s) do begin
  214. if s[a] = '.' then begin
  215. if b > maxnamefieldlen then exit;
  216. if (b = 0) then exit;
  217. packet.payload[a-b-1] := b;
  218. b := 0;
  219. end else begin
  220. packet.payload[a] := byte(s[a]);
  221. inc(b);
  222. end;
  223. end;
  224. if b > maxnamefieldlen then exit;
  225. packet.payload[length(s)-b] := b;
  226. result := length(s) + 12+5;
  227. end;
  228. arr[result-1] := 1;
  229. arr[result-3] := requesttype and $ff;
  230. arr[result-4] := requesttype shr 8;
  231. end;
  232. function makereversename(const binip:tbinip):string;
  233. var
  234. name:string;
  235. a,b:integer;
  236. begin
  237. name := '';
  238. if binip.family = AF_INET then begin
  239. b := htonl(binip.ip);
  240. for a := 0 to 3 do begin
  241. name := name + inttostr(b shr (a shl 3) and $ff)+'.';
  242. end;
  243. name := name + 'in-addr.arpa';
  244. end else
  245. {$ifdef ipv6}
  246. if binip.family = AF_INET6 then begin
  247. for a := 15 downto 0 do begin
  248. b := binip.ip6.u6_addr8[a];
  249. name := name + hexchars[b and $f]+'.'+hexchars[b shr 4]+'.';
  250. end;
  251. name := name + 'ip6.arpa';
  252. end else
  253. {$endif}
  254. begin
  255. {empty name}
  256. end;
  257. result := name;
  258. end;
  259. {
  260. decodes DNS format name to a string. does not includes the root dot.
  261. doesnt read beyond len.
  262. empty result + non null failurereason: failure
  263. empty result + null failurereason: internal use
  264. }
  265. function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):string;
  266. var
  267. arr:array[0..sizeof(packet)-1] of byte absolute packet;
  268. s:string;
  269. a,b:integer;
  270. begin
  271. numread := 0;
  272. repeat
  273. if (start+numread < 0) or (start+numread >= len) then begin
  274. result := '';
  275. failurereason := 'decoding name: got out of range1';
  276. exit;
  277. end;
  278. b := arr[start+numread];
  279. if b >= $c0 then begin
  280. {recursive sub call}
  281. if recursion > 10 then begin
  282. result := '';
  283. failurereason := 'decoding name: max recursion';
  284. exit;
  285. end;
  286. if ((start+numread+1) >= len) then begin
  287. result := '';
  288. failurereason := 'decoding name: got out of range3';
  289. exit;
  290. end;
  291. a := ((b shl 8) or arr[start+numread+1]) and $3fff;
  292. s := decodename(packet,len,a,recursion+1,a);
  293. if (s = '') and (failurereason <> '') then begin
  294. result := '';
  295. exit;
  296. end;
  297. if result <> '' then result := result + '.';
  298. result := result + s;
  299. inc(numread,2);
  300. exit;
  301. end else if b < 64 then begin
  302. if (numread <> 0) and (b <> 0) then result := result + '.';
  303. for a := start+numread+1 to start+numread+b do begin
  304. if (a >= len) then begin
  305. result := '';
  306. failurereason := 'decoding name: got out of range2';
  307. exit;
  308. end;
  309. result := result + char(arr[a]);
  310. end;
  311. inc(numread,b+1);
  312. if b = 0 then begin
  313. if (result = '') and (recursion = 0) then result := '.';
  314. exit; {reached end of name}
  315. end;
  316. end else begin
  317. failurereason := 'decoding name: read invalid char';
  318. result := '';
  319. exit; {invalid}
  320. end;
  321. until false;
  322. end;
  323. {==============================================================================}
  324. function getrawfromrr(const rrp:trrpointer;len:integer):string;
  325. begin
  326. setlength(result,htons(trr(rrp.p^).datalen));
  327. uniquestring(result);
  328. move(trr(rrp.p^).data,result[1],length(result));
  329. end;
  330. function getipfromrr(const rrp:trrpointer;len:integer):tbinip;
  331. begin
  332. fillchar(result,sizeof(result),0);
  333. case trr(rrp.p^).requesttype of
  334. querytype_a: begin
  335. if htons(trr(rrp.p^).datalen) <> 4 then exit;
  336. move(trr(rrp.p^).data,result.ip,4);
  337. result.family :=AF_INET;
  338. end;
  339. {$ifdef ipv6}
  340. querytype_aaaa: begin
  341. if htons(trr(rrp.p^).datalen) <> 16 then exit;
  342. result.family := AF_INET6;
  343. move(trr(rrp.p^).data,result.ip6,16);
  344. end;
  345. {$endif}
  346. else
  347. {}
  348. end;
  349. end;
  350. procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate);
  351. var
  352. a:integer;
  353. begin
  354. state.resultaction := action_done;
  355. state.resultstr := '';
  356. case trr(rrp.p^).requesttype of
  357. querytype_a{$ifdef ipv6},querytype_aaaa{$endif}: begin
  358. state.resultbin := getipfromrr(rrp,len);
  359. end;
  360. querytype_txt:begin
  361. {TXT returns a raw string}
  362. state.resultstr := copy(getrawfromrr(rrp,len),2,9999);
  363. fillchar(state.resultbin,sizeof(state.resultbin),0);
  364. end;
  365. querytype_mx:begin
  366. {MX is a name after a 16 bits word}
  367. state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+12,0,a);
  368. fillchar(state.resultbin,sizeof(state.resultbin),0);
  369. end;
  370. else
  371. {other reply types (PTR, MX) return a hostname}
  372. state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+10,0,a);
  373. fillchar(state.resultbin,sizeof(state.resultbin),0);
  374. end;
  375. end;
  376. procedure setstate_request_init(const name:string;var state:tdnsstate);
  377. begin
  378. {destroy things properly}
  379. state.resultstr := '';
  380. state.queryname := '';
  381. state.rrdata := '';
  382. fillchar(state,sizeof(state),0);
  383. state.queryname := name;
  384. state.parsepacket := false;
  385. end;
  386. procedure setstate_forward(const name:string;var state:tdnsstate;family:integer);
  387. begin
  388. setstate_request_init(name,state);
  389. state.forwardfamily := family;
  390. {$ifdef ipv6}
  391. if family = AF_INET6 then state.requesttype := querytype_aaaa else
  392. {$endif}
  393. state.requesttype := querytype_a;
  394. end;
  395. procedure setstate_reverse(const binip:tbinip;var state:tdnsstate);
  396. begin
  397. setstate_request_init(makereversename(binip),state);
  398. state.requesttype := querytype_ptr;
  399. end;
  400. procedure setstate_custom(const name:string; requesttype:integer; var state:tdnsstate);
  401. begin
  402. setstate_request_init(name,state);
  403. state.requesttype := requesttype;
  404. end;
  405. procedure setstate_failure(var state:tdnsstate);
  406. begin
  407. state.resultstr := '';
  408. fillchar(state.resultbin,sizeof(state.resultbin),0);
  409. state.resultaction := action_done;
  410. end;
  411. procedure state_process(var state:tdnsstate);
  412. label recursed;
  413. label failure;
  414. var
  415. a,b,ofs:integer;
  416. rrtemp:^trr;
  417. rrptemp:^trrpointer;
  418. begin
  419. if state.parsepacket then begin
  420. if state.recvpacketlen < 12 then begin
  421. failurereason := 'Undersized packet';
  422. state.resultaction := action_ignore;
  423. exit;
  424. end;
  425. if state.id <> state.recvpacket.id then begin
  426. failurereason := 'ID mismatch';
  427. state.resultaction := action_ignore;
  428. exit;
  429. end;
  430. state.numrr2 := 0;
  431. for a := 0 to 3 do begin
  432. state.numrr1[a] := htons(state.recvpacket.rrcount[a]);
  433. if state.numrr1[a] > maxrrofakind then goto failure;
  434. inc(state.numrr2,state.numrr1[a]);
  435. end;
  436. setlength(state.rrdata,state.numrr2*sizeof(trrpointer));
  437. {- put all replies into a list}
  438. ofs := 12;
  439. {get all queries}
  440. for a := 0 to state.numrr1[0]-1 do begin
  441. if (ofs < 12) or (ofs > state.recvpacketlen-4) then goto failure;
  442. rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
  443. rrptemp.p := @state.recvpacket.payload[ofs-12];
  444. rrptemp.ofs := ofs;
  445. decodename(state.recvpacket,state.recvpacketlen,ofs,0,b);
  446. rrptemp.len := b + 4;
  447. inc(ofs,rrptemp.len);
  448. end;
  449. for a := state.numrr1[0] to state.numrr2-1 do begin
  450. if (ofs < 12) or (ofs > state.recvpacketlen-12) then goto failure;
  451. rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
  452. if decodename(state.recvpacket,state.recvpacketlen,ofs,0,b) = '' then goto failure;
  453. rrtemp := @state.recvpacket.payload[ofs-12+b]; {rrtemp points to values and result, after initial name}
  454. rrptemp.p := rrtemp;
  455. rrptemp.ofs := ofs; {ofs is start of RR before initial name from start of packet}
  456. rrptemp.namelen := b;
  457. b := htons(rrtemp.datalen);
  458. rrptemp.len := b + 10 + rrptemp.namelen;
  459. inc(ofs,rrptemp.len);
  460. end;
  461. if (ofs <> state.recvpacketlen) then begin
  462. failurereason := 'ofs <> state.packetlen';
  463. goto failure;
  464. end;
  465. {if we requested A or AAAA build a list of all replies}
  466. if (state.requesttype = querytype_a) or (state.requesttype = querytype_aaaa) then begin
  467. state.resultlist := biniplist_new;
  468. for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin
  469. rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
  470. rrtemp := rrptemp.p;
  471. b := rrptemp.len;
  472. if rrtemp.requesttype = state.requesttype then begin
  473. biniplist_add(state.resultlist,getipfromrr(rrptemp^,b));
  474. end;
  475. end;
  476. end;
  477. {- check for items of the requested type in answer section, if so return success first}
  478. for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin
  479. rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
  480. rrtemp := rrptemp.p;
  481. b := rrptemp.len;
  482. if rrtemp.requesttype = state.requesttype then begin
  483. setstate_return(rrptemp^,b,state);
  484. exit;
  485. end;
  486. end;
  487. {if no items of correct type found, follow first cname in answer section}
  488. for a := state.numrr1[0] to (state.numrr1[0]+state.numrr1[1]-1) do begin
  489. rrptemp := @state.rrdata[1+a*sizeof(trrpointer)];
  490. rrtemp := rrptemp.p;
  491. b := rrptemp.len;
  492. if rrtemp.requesttype = querytype_cname then begin
  493. state.queryname := decodename(state.recvpacket,state.recvpacketlen,rrptemp.ofs+12,0,b);
  494. goto recursed;
  495. end;
  496. end;
  497. {no cnames found, no items of correct type found}
  498. if state.forwardfamily <> 0 then goto failure;
  499. goto failure;
  500. recursed:
  501. {here it needs recursed lookup}
  502. {if needing to follow a cname, change state to do so}
  503. inc(state.recursioncount);
  504. if state.recursioncount > maxrecursion then goto failure;
  505. end;
  506. {here, a name needs to be resolved}
  507. if state.queryname = '' then begin
  508. failurereason := 'empty query name';
  509. goto failure;
  510. end;
  511. {do /ets/hosts lookup here}
  512. state.sendpacketlen := buildrequest(state.queryname,state.sendpacket,state.requesttype);
  513. if state.sendpacketlen = 0 then begin
  514. failurereason := 'building request packet failed';
  515. goto failure;
  516. end;
  517. state.id := state.sendpacket.id;
  518. state.resultaction := action_sendquery;
  519. exit;
  520. failure:
  521. setstate_failure(state);
  522. end;
  523. {$ifdef win32}
  524. const
  525. MAX_HOSTNAME_LEN = 132;
  526. MAX_DOMAIN_NAME_LEN = 132;
  527. MAX_SCOPE_ID_LEN = 260 ;
  528. MAX_ADAPTER_NAME_LENGTH = 260;
  529. MAX_ADAPTER_ADDRESS_LENGTH = 8;
  530. MAX_ADAPTER_DESCRIPTION_LENGTH = 132;
  531. ERROR_BUFFER_OVERFLOW = 111;
  532. MIB_IF_TYPE_ETHERNET = 6;
  533. MIB_IF_TYPE_TOKENRING = 9;
  534. MIB_IF_TYPE_FDDI = 15;
  535. MIB_IF_TYPE_PPP = 23;
  536. MIB_IF_TYPE_LOOPBACK = 24;
  537. MIB_IF_TYPE_SLIP = 28;
  538. type
  539. tip_addr_string=packed record
  540. Next :pointer;
  541. IpAddress : array[0..15] of char;
  542. ipmask : array[0..15] of char;
  543. context : dword;
  544. end;
  545. pip_addr_string=^tip_addr_string;
  546. tFIXED_INFO=packed record
  547. HostName : array[0..MAX_HOSTNAME_LEN-1] of char;
  548. DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of char;
  549. currentdnsserver : pip_addr_string;
  550. dnsserverlist : tip_addr_string;
  551. nodetype : longint;
  552. ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of char;
  553. enablerouting : longbool;
  554. enableproxy : longbool;
  555. enabledns : longbool;
  556. end;
  557. pFIXED_INFO=^tFIXED_INFO;
  558. var
  559. iphlpapi : thandle;
  560. getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall;
  561. {$endif}
  562. procedure populatednsserverlist;
  563. var
  564. {$ifdef win32}
  565. fixed_info : pfixed_info;
  566. fixed_info_len : longint;
  567. currentdnsserver : pip_addr_string;
  568. {$else}
  569. t:textfile;
  570. s:string;
  571. a:integer;
  572. {$endif}
  573. begin
  574. //result := '';
  575. if assigned(dnsserverlist) then begin
  576. dnsserverlist.clear;
  577. end else begin
  578. dnsserverlist := tstringlist.Create;
  579. end;
  580. {$ifdef win32}
  581. if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll');
  582. if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams');
  583. if not assigned(getnetworkparams) then exit;
  584. fixed_info_len := 0;
  585. if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit;
  586. //fixed_info_len :=sizeof(tfixed_info);
  587. getmem(fixed_info,fixed_info_len);
  588. if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin
  589. freemem(fixed_info);
  590. exit;
  591. end;
  592. currentdnsserver := @(fixed_info.dnsserverlist);
  593. while assigned(currentdnsserver) do begin
  594. dnsserverlist.Add(currentdnsserver.IpAddress);
  595. currentdnsserver := currentdnsserver.next;
  596. end;
  597. freemem(fixed_info);
  598. {$else}
  599. filemode := 0;
  600. assignfile(t,'/etc/resolv.conf');
  601. {$i-}reset(t);{$i+}
  602. if ioresult <> 0 then exit;
  603. while not eof(t) do begin
  604. readln(t,s);
  605. if not (copy(s,1,10) = 'nameserver') then continue;
  606. s := copy(s,11,500);
  607. while s <> '' do begin
  608. if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break;
  609. end;
  610. a := pos(' ',s);
  611. if a <> 0 then s := copy(s,1,a-1);
  612. a := pos(#9,s);
  613. if a <> 0 then s := copy(s,1,a-1);
  614. //result := s;
  615. //if result <> '' then break;
  616. dnsserverlist.Add(s);
  617. end;
  618. close(t);
  619. {$endif}
  620. end;
  621. procedure cleardnsservercache;
  622. begin
  623. if assigned(dnsserverlist) then begin
  624. dnsserverlist.destroy;
  625. dnsserverlist := nil;
  626. end;
  627. end;
  628. function getcurrentsystemnameserver(var id:integer):string;
  629. var
  630. counter : integer;
  631. begin
  632. if not assigned(dnsserverlist) then populatednsserverlist;
  633. if dnsserverlist.count=0 then raise exception.create('no dns servers availible');
  634. id := 0;
  635. if dnsserverlist.count >1 then begin
  636. for counter := 1 to dnsserverlist.count-1 do begin
  637. if taddrint(dnsserverlist.objects[counter]) < taddrint(dnsserverlist.objects[id]) then id := counter;
  638. end;
  639. end;
  640. result := dnsserverlist[id]
  641. end;
  642. procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and should be -1 to report a timeout
  643. var
  644. counter : integer;
  645. temp : integer;
  646. begin
  647. if (id < 0) or (id >= dnsserverlist.count) then exit;
  648. if lag = -1 then lag := timeoutlag;
  649. for counter := 0 to dnsserverlist.count-1 do begin
  650. temp := taddrint(dnsserverlist.objects[counter]) *15;
  651. if counter=id then temp := temp + lag;
  652. dnsserverlist.objects[counter] := tobject(temp div 16);
  653. end;
  654. end;
  655. {$ifdef ipv6}
  656. {$ifdef linux}
  657. function getv6localips:tbiniplist;
  658. var
  659. t:textfile;
  660. s,s2:string;
  661. ip:tbinip;
  662. a:integer;
  663. begin
  664. result := biniplist_new;
  665. assignfile(t,'/proc/net/if_inet6');
  666. {$i-}reset(t);{$i+}
  667. if ioresult <> 0 then exit; {none found, return empty list}
  668. while not eof(t) do begin
  669. readln(t,s);
  670. s2 := '';
  671. for a := 0 to 7 do begin
  672. if (s2 <> '') then s2 := s2 + ':';
  673. s2 := s2 + copy(s,(a shl 2)+1,4);
  674. end;
  675. ipstrtobin(s2,ip);
  676. if ip.family <> 0 then biniplist_add(result,ip);
  677. end;
  678. closefile(t);
  679. end;
  680. {$else}
  681. function getv6localips:tbiniplist;
  682. begin
  683. result := biniplist_new;
  684. end;
  685. {$endif}
  686. procedure initpreferredmode;
  687. var
  688. l:tbiniplist;
  689. a:integer;
  690. ip:tbinip;
  691. ipmask_global,ipmask_6to4,ipmask_teredo:tbinip;
  692. begin
  693. if preferredmodeinited then exit;
  694. if useaf <> useaf_default then exit;
  695. l := getv6localips;
  696. if biniplist_getcount(l) = 0 then exit;
  697. useaf := useaf_preferv4;
  698. ipstrtobin('2000::',ipmask_global);
  699. ipstrtobin('2001::',ipmask_teredo);
  700. ipstrtobin('2002::',ipmask_6to4);
  701. {if there is any v6 IP which is globally routable and not 6to4 and not teredo, prefer v6}
  702. for a := biniplist_getcount(l)-1 downto 0 do begin
  703. ip := biniplist_get(l,a);
  704. if not comparebinipmask(ip,ipmask_global,3) then continue;
  705. if comparebinipmask(ip,ipmask_teredo,32) then continue;
  706. if comparebinipmask(ip,ipmask_6to4,16) then continue;
  707. useaf := useaf_preferv6;
  708. preferredmodeinited := true;
  709. exit;
  710. end;
  711. end;
  712. {$endif}
  713. { quick and dirty description of dns packet structure to aid writing and
  714. understanding of parser code, refer to appropriate RFCs for proper specs
  715. - all words are network order
  716. www.google.com A request:
  717. 0, 2: random transaction ID
  718. 2, 2: flags: only the "recursion desired" bit set. (bit 8 of word)
  719. 4, 2: questions: 1
  720. 6, 2: answer RR's: 0.
  721. 8, 2: authority RR's: 0.
  722. 10, 2: additional RR's: 0.
  723. 12, n: payload:
  724. query:
  725. #03 "www" #06 "google" #03 "com" #00
  726. size-4, 2: type: host address (1)
  727. size-2, 2: class: inet (1)
  728. reply:
  729. 0,2: random transaction ID
  730. 2,2: flags: set: response (bit 15), recursion desired (8), recursion available (7)
  731. 4,4: questions: 1
  732. 6,4: answer RR's: 2
  733. 8,4: authority RR's: 9
  734. 10,4: additional RR's: 9
  735. 12: payload:
  736. query:
  737. ....
  738. answer: CNAME
  739. 0,2 "c0 0c" "name: www.google.com"
  740. 2,2 "00 05" "type: cname for an alias"
  741. 4,2 "00 01" "class: inet"
  742. 6,4: TTL
  743. 10,2: data length "00 17" (23)
  744. 12: the cname name (www.google.akadns.net)
  745. answer: A
  746. 0,2 ..
  747. 2,2 "00 01" host address
  748. 4,2 ...
  749. 6,4 ...
  750. 10,2: data length (4)
  751. 12,4: binary IP
  752. authority - 9 records
  753. additional - 9 records
  754. ipv6 AAAA reply:
  755. 0,2: ...
  756. 2,2: type: 001c
  757. 4,2: class: inet (0001)
  758. 6,2: TTL
  759. 10,2: data size (16)
  760. 12,16: binary IP
  761. ptr request: query type 000c
  762. name compression: word "cxxx" in the name, xxx points to offset in the packet}
  763. end.