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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. unit dnswin;
  2. interface
  3. uses binipstuff,classes,lcore;
  4. {$include lcoreconfig.inc}
  5. //on failure a null string or zeroed out binip will be retuned and error will be
  6. //set to a windows error code (error will be left untouched under non error
  7. //conditions).
  8. function winforwardlookuplist(name : string;familyhint:integer;var error : integer) : tbiniplist;
  9. function winreverselookup(ip:tbinip;var error:integer):string;
  10. type
  11. //do not call destroy on a tdnswinasync instead call release and the
  12. //dnswinasync will be freed when appropriate. Calling destroy will block
  13. //the calling thread until the dns lookup completes.
  14. //release should only be called from the main thread
  15. tdnswinasync=class(tthread)
  16. private
  17. freverse : boolean;
  18. error : integer;
  19. freewhendone : boolean;
  20. hadevent : boolean;
  21. protected
  22. procedure execute; override;
  23. public
  24. onrequestdone:tsocketevent;
  25. name : string;
  26. iplist : tbiniplist;
  27. procedure forwardlookup(name:string);
  28. procedure reverselookup(ip:tbinip);
  29. destructor destroy; override;
  30. procedure release;
  31. constructor create;
  32. property reverse : boolean read freverse;
  33. end;
  34. implementation
  35. uses
  36. lsocket,pgtypes,sysutils,winsock,windows,messages;
  37. type
  38. //taddrinfo = record; //forward declaration
  39. paddrinfo = ^taddrinfo;
  40. taddrinfo = packed record
  41. ai_flags : longint;
  42. ai_family : longint;
  43. ai_socktype : longint;
  44. ai_protocol : longint;
  45. ai_addrlen : taddrint;
  46. ai_canonname : pchar;
  47. ai_addr : pinetsockaddrv;
  48. ai_next : paddrinfo;
  49. end;
  50. ppaddrinfo = ^paddrinfo;
  51. tgetaddrinfo = function(nodename : pchar; servname : pchar; hints : paddrinfo;res : ppaddrinfo) : longint; stdcall;
  52. tfreeaddrinfo = procedure(ai : paddrinfo); stdcall;
  53. tgetnameinfo = function(sa:Pinetsockaddrv;salen : longint; host:pchar;hostlen : longint;serv:pchar;servlen:longint;flags:longint) : longint;stdcall;
  54. var
  55. getaddrinfo : tgetaddrinfo;
  56. freeaddrinfo : tfreeaddrinfo;
  57. getnameinfo : tgetnameinfo;
  58. procedure v4onlyfreeaddrinfo(ai : paddrinfo); stdcall;
  59. var
  60. next:paddrinfo;
  61. begin
  62. while assigned(ai) do begin
  63. freemem(ai.ai_addr);
  64. next := ai.ai_next;
  65. freemem(ai);
  66. ai := next;
  67. end;
  68. end;
  69. type
  70. plongint = ^longint;
  71. pplongint = ^plongint;
  72. function v4onlygetaddrinfo(nodename : pchar; servname : pchar; hints : paddrinfo;res : ppaddrinfo) : longint; stdcall;
  73. var
  74. output,prev,first : paddrinfo;
  75. hostent : phostent;
  76. addrlist:^pointer;
  77. begin
  78. if hints.ai_family <> af_inet6 then begin
  79. result := 0;
  80. hostent := gethostbyname(nodename);
  81. if hostent = nil then begin
  82. result := wsagetlasterror;
  83. v4onlyfreeaddrinfo(output);
  84. exit;
  85. end;
  86. addrlist := pointer(hostent.h_addr_list);
  87. //ipint := pplongint(hostent.h_addr_list)^^;
  88. prev := nil;
  89. first := nil;
  90. repeat
  91. if not assigned(addrlist^) then break;
  92. getmem(output,sizeof(taddrinfo));
  93. if assigned(prev) then prev.ai_next := output;
  94. getmem(output.ai_addr,sizeof(tinetsockaddr));
  95. if servname <> nil then output.ai_addr.InAddr.port := htons(strtoint(servname)) else output.ai_addr.InAddr.port := 0;
  96. output.ai_addr.InAddr.addr := longint(addrlist^^);
  97. inc(integer(addrlist),4);
  98. output.ai_flags := 0;
  99. output.ai_family := af_inet;
  100. output.ai_socktype := 0;
  101. output.ai_protocol := 0;
  102. output.ai_addrlen := sizeof(tinetsockaddr);
  103. output.ai_canonname := nil;
  104. output.ai_next := nil;
  105. prev := output;
  106. if not assigned(first) then first := output;
  107. until false;
  108. res^ := first;
  109. end else begin
  110. result := WSANO_RECOVERY;
  111. end;
  112. end;
  113. function min(a,b : integer):integer;
  114. begin
  115. if a<b then result := a else result := b;
  116. end;
  117. function v4onlygetnameinfo(sa:Pinetsockaddrv;salen : longint; host:pchar;hostlen : longint;serv:pchar;servlen:longint;flags:longint) : longint;stdcall;
  118. var
  119. hostent : phostent;
  120. bytestocopy : integer;
  121. begin
  122. if sa.InAddr.family = af_inet then begin
  123. result := 0;
  124. hostent := gethostbyaddr(@(sa.inaddr.addr),4,AF_INET);
  125. if hostent = nil then begin
  126. result := wsagetlasterror;
  127. exit;
  128. end;
  129. bytestocopy := min(strlen(hostent.h_name)+1,hostlen);
  130. move((hostent.h_name)^,host^,bytestocopy);
  131. end else begin
  132. result := WSANO_RECOVERY;
  133. end;
  134. end;
  135. procedure populateprocvars;
  136. var
  137. libraryhandle : hmodule;
  138. i : integer;
  139. dllname : string;
  140. begin
  141. if assigned(getaddrinfo) then exit; //procvars already populated
  142. for i := 0 to 1 do begin
  143. if i=0 then dllname := 'Ws2_32.dll' else dllname := 'Wship6.dll';
  144. libraryhandle := LoadLibrary(pchar(dllname));
  145. getaddrinfo := getprocaddress(libraryhandle,'getaddrinfo');
  146. freeaddrinfo := getprocaddress(libraryhandle,'freeaddrinfo');
  147. getnameinfo := getprocaddress(libraryhandle,'getnameinfo');
  148. if assigned(getaddrinfo) and assigned(freeaddrinfo) and assigned(getnameinfo) then begin
  149. //writeln('found getaddrinfo and freeaddrinfo in'+dllname);
  150. exit; //success
  151. end;
  152. end;
  153. //writeln('could not find getaddrinfo and freeaddrinfo, falling back to ipv4 only lookup');
  154. getaddrinfo := v4onlygetaddrinfo;
  155. freeaddrinfo := v4onlyfreeaddrinfo;
  156. getnameinfo := v4onlygetnameinfo;
  157. end;
  158. function winforwardlookuplist(name : string;familyhint:integer;var error : integer) : tbiniplist;
  159. var
  160. hints: taddrinfo;
  161. res0,res : paddrinfo;
  162. getaddrinforesult : integer;
  163. biniptemp:tbinip;
  164. begin
  165. populateprocvars;
  166. hints.ai_flags := 0;
  167. hints.ai_family := familyhint;
  168. hints.ai_socktype := 0;
  169. hints.ai_protocol := 0;
  170. hints.ai_addrlen := 0;
  171. hints.ai_canonname := nil;
  172. hints.ai_addr := nil;
  173. hints.ai_next := nil;
  174. getaddrinforesult := getaddrinfo(pchar(name),'1',@hints,@res);
  175. res0 := res;
  176. result := biniplist_new;
  177. if getaddrinforesult = 0 then begin
  178. while assigned(res) do begin
  179. if res.ai_family = af_inet then begin
  180. biniptemp.family := af_inet;
  181. biniptemp.ip := res.ai_addr.InAddr.addr;
  182. biniplist_add(result,biniptemp);
  183. {$ifdef ipv6}
  184. end else if res.ai_family = af_inet6 then begin
  185. biniptemp.family := af_inet6;
  186. biniptemp.ip6 := res.ai_addr.InAddr6.sin6_addr;
  187. biniplist_add(result,biniptemp);
  188. {$endif}
  189. end;
  190. res := res.ai_next;
  191. end;
  192. freeaddrinfo(res0);
  193. exit;
  194. end;
  195. if getaddrinforesult <> 0 then begin
  196. fillchar(result,0,sizeof(result));
  197. error := getaddrinforesult;
  198. end;
  199. end;
  200. function winreverselookup(ip:tbinip;var error : integer):string;
  201. var
  202. sa : tinetsockaddrv;
  203. getnameinforesult : integer;
  204. begin
  205. if ip.family = AF_INET then begin
  206. sa.InAddr.family := AF_INET;
  207. sa.InAddr.port := 1;
  208. sa.InAddr.addr := ip.ip;
  209. end else {$ifdef ipv6}if ip.family = AF_INET6 then begin
  210. sa.InAddr6.sin6_family := AF_INET6;
  211. sa.InAddr6.sin6_port := 1;
  212. sa.InAddr6.sin6_addr := ip.ip6;
  213. end else{$endif} begin
  214. raise exception.create('unrecognised address family');
  215. end;
  216. populateprocvars;
  217. setlength(result,1025);
  218. getnameinforesult := getnameinfo(@sa,sizeof(tinetsockaddrv),pchar(result),length(result),nil,0,0);
  219. if getnameinforesult <> 0 then begin
  220. error := getnameinforesult;
  221. result := '';
  222. exit;
  223. end;
  224. if pos(#0,result) >= 0 then begin
  225. setlength(result,pos(#0,result)-1);
  226. end;
  227. end;
  228. var
  229. hwnddnswin : hwnd;
  230. function MyWindowProc(
  231. ahWnd : HWND;
  232. auMsg : Integer;
  233. awParam : WPARAM;
  234. alParam : LPARAM): Integer; stdcall;
  235. var
  236. dwas : tdnswinasync;
  237. begin
  238. if (ahwnd=hwnddnswin) and (aumsg=wm_user) then begin
  239. Dwas := tdnswinasync(alparam);
  240. if assigned (dwas.onrequestdone) then dwas.onrequestdone(dwas,awparam);
  241. dwas.hadevent := true;
  242. if dwas.freewhendone then dwas.free;
  243. end else begin
  244. //not passing unknown messages on to defwindowproc will cause window
  245. //creation to fail! --plugwash
  246. Result := DefWindowProc(ahWnd, auMsg, awParam, alParam)
  247. end;
  248. end;
  249. procedure tdnswinasync.forwardlookup(name:string);
  250. begin
  251. self.name := name;
  252. freverse := false;
  253. resume;
  254. end;
  255. procedure tdnswinasync.reverselookup(ip:tbinip);
  256. begin
  257. iplist := biniplist_new;
  258. biniplist_add(iplist,ip);
  259. freverse := true;
  260. resume;
  261. end;
  262. procedure tdnswinasync.execute;
  263. var
  264. error : integer;
  265. begin
  266. error := 0;
  267. if reverse then begin
  268. name := winreverselookup(biniplist_get(iplist,0),error);
  269. end else begin
  270. iplist := winforwardlookuplist(name,0,error);
  271. end;
  272. postmessage(hwnddnswin,wm_user,error,taddrint(self));
  273. end;
  274. destructor tdnswinasync.destroy;
  275. begin
  276. WaitFor;
  277. inherited destroy;
  278. end;
  279. procedure tdnswinasync.release;
  280. begin
  281. if hadevent then destroy else begin
  282. onrequestdone := nil;
  283. freewhendone := true;
  284. end;
  285. end;
  286. constructor tdnswinasync.create;
  287. begin
  288. inherited create(true);
  289. end;
  290. var
  291. MyWindowClass : TWndClass = (style : 0;
  292. lpfnWndProc : @MyWindowProc;
  293. cbClsExtra : 0;
  294. cbWndExtra : 0;
  295. hInstance : 0;
  296. hIcon : 0;
  297. hCursor : 0;
  298. hbrBackground : 0;
  299. lpszMenuName : nil;
  300. lpszClassName : 'dnswinClass');
  301. begin
  302. if Windows.RegisterClass(MyWindowClass) = 0 then halt;
  303. //writeln('about to create lcore handle, hinstance=',hinstance);
  304. hwnddnswin := CreateWindowEx(WS_EX_TOOLWINDOW,
  305. MyWindowClass.lpszClassName,
  306. '', { Window name }
  307. WS_POPUP, { Window Style }
  308. 0, 0, { X, Y }
  309. 0, 0, { Width, Height }
  310. 0, { hWndParent }
  311. 0, { hMenu }
  312. HInstance, { hInstance }
  313. nil); { CreateParam }
  314. //writeln('dnswin hwnd is ',hwnddnswin);
  315. //writeln('last error is ',GetLastError);
  316. end.