* fixed NT services not working. app must now call lcoreinit() at some point before...
authorbeware <beware@bircd.org>
Fri, 26 Dec 2008 19:17:00 +0000 (19:17 +0000)
committerbeware <beware@bircd.org>
Fri, 26 Dec 2008 19:17:00 +0000 (19:17 +0000)
* made dnssync and dnsasync secure with source port randomization and reply packet source IP/port verification
* created lcorerandom, a secure general purpose random number source, replacement of bircrandom
* added fastmd5.pas into the repository. it wasn't in it, but seemed to belong in it and lcorernd depends on it.
* added the ability to do "custom" (txt, mx, ns, ptr, etc) lookups in dnscore and dnsasync
* lsocket.receivefrom now converts a v6 mapped v4 IP to a real v4 IP for simplicity in the app
* removed "ipv6preferred" from dnswin, which was doing nothing

git-svn-id: file:///svnroot/lcore/trunk@20 b1de8a11-f9be-4011-bde0-cc7ace90066a

12 files changed:
binipstuff.pas
dnsasync.pas
dnscore.pas
dnssync.pas
dnswin.pas
fastmd5.pas [new file with mode: 0644]
lcoreconfig.inc
lcorernd.pas [new file with mode: 0644]
lcoreselect.pas
lcorewsaasyncselect.pas
lsocket.pas
todo.txt

index a1433fcc25897c54a057bf667046e868ddd81615..70ac40121308897df5c63a32cc77094b666611be 100755 (executable)
@@ -146,6 +146,7 @@ procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);
 {deprecated}\r
 function longip(s:string):longint;\r
 \r
+function needconverttov4(const ip:tbinip):boolean;\r
 procedure converttov4(var ip:tbinip);\r
 \r
 function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;\r
@@ -501,18 +502,30 @@ begin
   result := comparebinip(ip1,ip2);\r
 end;\r
 \r
-{converts a binary IP to v4 if it is a v6 IP in the v4 range}\r
-procedure converttov4(var ip:tbinip);\r
+function needconverttov4(const ip:tbinip):boolean;\r
 begin\r
   {$ifdef ipv6}\r
   if ip.family = AF_INET6 then begin\r
     if (ip.ip6.u6_addr32[0] = 0) and (ip.ip6.u6_addr32[1] = 0) and\r
     (ip.ip6.u6_addr16[4] = 0) and (ip.ip6.u6_addr16[5] = $ffff) then begin\r
-      ip.family := AF_INET;\r
-      ip.ip := ip.ip6.s6_addr32[3];\r
+      result := true;\r
+      exit;\r
     end;\r
   end;\r
   {$endif}\r
+\r
+  result := false;\r
+end;\r
+\r
+{converts a binary IP to v4 if it is a v6 IP in the v4 range}\r
+procedure converttov4(var ip:tbinip);\r
+begin\r
+  {$ifdef ipv6}\r
+  if needconverttov4(ip) then begin\r
+    ip.family := AF_INET;\r
+    ip.ip := ip.ip6.s6_addr32[3];\r
+  end;\r
+  {$endif}\r
 end;\r
 \r
 {-----------biniplist stuff--------------------------------------------------}\r
index 7a10bbfdc7cc0466942906c68617712309317e22..8c3ce3a01d4afca44a754a3f268ca826fb6452ed 100755 (executable)
@@ -15,7 +15,9 @@ uses
     dnswin,\r
   {$endif}\r
   lsocket,lcore,\r
-  classes,binipstuff,dnscore,btime;\r
+  classes,binipstuff,dnscore,btime,lcorernd;\r
+\r
+{$include lcoreconfig.inc}\r
 \r
 const\r
   numsock=1{$ifdef ipv6}+1{$endif};\r
@@ -33,6 +35,8 @@ type
 \r
     states: array[0..numsock-1] of tdnsstate;\r
 \r
+    destinations: array[0..numsock-1] of tbinip;\r
+\r
     dnsserverids : array[0..numsock-1] of integer;\r
     startts:double;\r
     {$ifdef win32}\r
@@ -60,11 +64,6 @@ type
 \r
     overrideaf : integer;\r
 \r
-    //A family value of AF_INET6 will give only\r
-    //ipv6 results. Any other value will give ipv4 results in preference and ipv6\r
-    //results if ipv4 results are not available;\r
-    forwardfamily:integer;\r
-\r
     procedure cancel;//cancel an outstanding dns request\r
     function dnsresult:string; //get result of dnslookup as a string\r
     procedure dnsresultbin(var binip:tbinip); //get result of dnslookup as a tbinip\r
@@ -72,6 +71,7 @@ type
     procedure forwardlookup(const name:string); //start forward lookup,\r
                                                 //preffering ipv4\r
     procedure reverselookup(const binip:tbinip); //start reverse lookup\r
+    procedure customlookup(const name:string;querytype:integer); //start custom type lookup\r
 \r
     constructor create(aowner:tcomponent); override;\r
     destructor destroy; override;\r
@@ -113,16 +113,31 @@ end;
 procedure tdnsasync.receivehandler(sender:tobject;error:word);\r
 var\r
   socketno : integer;\r
+  Src    : TInetSockAddrV;\r
+  SrcLen : Integer;\r
+  fromip:tbinip;\r
+  fromport:string;\r
 begin\r
   socketno := tlsocket(sender).tag;\r
   //writeln('got a reply on socket number ',socketno);\r
   fillchar(states[socketno].recvpacket,sizeof(states[socketno].recvpacket),0);\r
-  states[socketno].recvpacketlen := twsocket(sender).Receive(@(states[socketno].recvpacket), SizeOf(states[socketno].recvpacket));\r
+\r
+  SrcLen := SizeOf(Src);\r
+  states[socketno].recvpacketlen := twsocket(sender).ReceiveFrom(@(states[socketno].recvpacket), SizeOf(states[socketno].recvpacket), Src, SrcLen);\r
+\r
+  fromip := inaddrvtobinip(Src);\r
+  fromport := inttostr(htons(src.InAddr.port));\r
+\r
+  if ((not comparebinip(fromip,destinations[socketno])) or (fromport <> port)) then begin\r
+   // writeln('dnsasync received from wrong IP:port ',ipbintostr(fromip),'#',fromport,', expected ',ipbintostr(destinations[socketno]),'#',port);\r
+    exit;\r
+  end;\r
+\r
   states[socketno].parsepacket := true;\r
   if states[socketno].resultaction <> action_done then begin\r
     //we ignore packets that come after we are done\r
     if dnsserverids[socketno] >= 0 then begin\r
-      reportlag(dnsserverids[socketno],trunc((unixtimefloat-startts)*1000));\r
+      reportlag(dnsserverids[socketno],trunc((unixtimefloat-startts)*1000000));\r
       dnsserverids[socketno] := -1;\r
     end;\r
   {  writeln('received reply');}\r
@@ -138,6 +153,7 @@ function tdnsasync.sendquery(socketno:integer;const packet:tdnspacket;len:intege
 var\r
   destination : string;\r
   inaddr : tinetsockaddrv;\r
+  trytolisten:integer;\r
 begin\r
 {  writeln('sendquery ',decodename(state.packet,state.packetlen,12,0,a),' ',state.requesttype);}\r
   //writeln('trying to send query on socket number ',socketno);\r
@@ -148,11 +164,20 @@ begin
     if port = '' then port := '53';\r
     sockets[socketno].Proto := 'udp';\r
     sockets[socketno].ondataavailable := receivehandler;\r
-    try\r
-      sockets[socketno].listen;\r
-    except\r
-      result := false;\r
-      exit;\r
+\r
+    {we are going to bind on a random local port for the DNS request, against the kaminsky attack\r
+    there is a small chance that we're trying to bind on an already used port, so retry a few times}\r
+    for trytolisten := 3 downto 0 do begin\r
+      try\r
+        sockets[socketno].port := inttostr(1024 + randominteger(65536 - 1024));\r
+        sockets[socketno].listen;\r
+      except\r
+        {writeln('failed to listen ',sockets[socketno].localport,' ',trytolisten);}\r
+        if (trytolisten = 0) then begin\r
+          result := false;\r
+          exit;\r
+        end;\r
+      end;\r
     end;\r
 \r
   end;\r
@@ -162,7 +187,13 @@ begin
   end else begin\r
     destination := getcurrentsystemnameserver(dnsserverids[socketno]);\r
   end;\r
-  makeinaddrv(ipstrtobinf(destination),port,inaddr);\r
+  destinations[socketno] := ipstrtobinf(destination);\r
+\r
+  {$ifdef ipv6}{$ifdef win32}\r
+  if destinations[socketno].family = AF_INET6 then if (requestaf = useaf_default) then requestaf := useaf_preferv6;\r
+  {$endif}{$endif}\r
+\r
+  makeinaddrv(destinations[socketno],port,inaddr);\r
   sockets[socketno].sendto(inaddr,sizeof(inaddr), @packet,len);\r
   result := true;\r
 \r
@@ -212,7 +243,6 @@ var
   bip : tbinip;\r
   i : integer;\r
 begin\r
-\r
   ipstrtobin(name,bip);\r
 \r
   if bip.family <> 0 then begin\r
@@ -223,22 +253,25 @@ begin
     exit;\r
   end;\r
 \r
+  if (overridednsserver <> '') and (addr = '') then addr := overridednsserver;\r
+\r
   if overrideaf = useaf_default then begin\r
-    {$ifdef linux}{$ifdef ipv6}initpreferredmode;{$endif}{$endif}\r
+    {$ifdef ipv6}\r
+      {$ifdef win32}if not (usewindns and (addr = '')) then{$endif}\r
+      initpreferredmode;\r
+    {$endif}\r
     requestaf := useaf;\r
   end else begin\r
     requestaf := overrideaf;\r
   end;\r
 \r
   {$ifdef win32}\r
-    if usewindns or (addr = '') then begin\r
+    if usewindns and (addr = '') then begin\r
       dwas := tdnswinasync.create;\r
       dwas.onrequestdone := winrequestdone;\r
-      if forwardfamily = AF_INET6 then begin\r
-        dwas.forwardlookup(name,true);\r
-      end else begin\r
-        dwas.forwardlookup(name,false);\r
-      end;\r
+\r
+      dwas.forwardlookup(name);\r
+\r
       exit;\r
     end;\r
   {$endif}\r
@@ -263,10 +296,10 @@ begin
 end;\r
 \r
 procedure tdnsasync.reverselookup;\r
-\r
 begin\r
+  if (overridednsserver <> '') and (addr = '') then addr := overridednsserver;\r
   {$ifdef win32}\r
-    if usewindns or (addr = '') then begin\r
+    if usewindns and (addr = '') then begin\r
       dwas := tdnswinasync.create;\r
       dwas.onrequestdone := winrequestdone;\r
       dwas.reverselookup(binip);\r
@@ -279,6 +312,14 @@ begin
   asyncprocess(0);\r
 end;\r
 \r
+procedure tdnsasync.customlookup;\r
+begin\r
+  if (overridednsserver <> '') and (addr = '') then addr := overridednsserver;\r
+  setstate_custom(name,querytype,states[0]);\r
+  numsockused := 1;\r
+  asyncprocess(0);\r
+end;\r
+\r
 function tdnsasync.dnsresult;\r
 begin\r
   if states[0].resultstr <> '' then result := states[0].resultstr else begin\r
@@ -322,7 +363,7 @@ end;
   procedure tdnsasync.winrequestdone(sender:tobject;error:word);\r
  \r
   begin\r
-    if dwas.reverse then begin \r
+    if dwas.reverse then begin\r
       states[0].resultstr := dwas.name;\r
     end else begin \r
 \r
index 600581dface0910f25ddabd48b44fdfbf31e6880..4cb52e2461231bab9f3a34a547e0ee9290f0f44d 100755 (executable)
@@ -60,7 +60,7 @@ unit dnscore;
 \r
 interface\r
 \r
-uses binipstuff,classes,pgtypes;\r
+uses binipstuff,classes,pgtypes,lcorernd;\r
 \r
 var usewindns : boolean = {$ifdef win32}true{$else}false{$endif};\r
 {hint to users of this unit that they should use windows dns instead.\r
@@ -82,6 +82,11 @@ can be set by apps as desired
 }\r
 var useaf:integer = useaf_default;\r
 \r
+{\r
+(temporarily) use a different nameserver, regardless of the dnsserverlist\r
+}\r
+var overridednsserver:string;\r
+\r
 const\r
   maxnamelength=127;\r
   maxnamefieldlen=63;\r
@@ -93,12 +98,14 @@ const
   querytype_a=1;\r
   querytype_cname=5;\r
   querytype_aaaa=28;\r
+  querytype_a6=38;\r
   querytype_ptr=12;\r
   querytype_ns=2;\r
   querytype_soa=6;\r
   querytype_mx=15;\r
-\r
-  maxrecursion=10;\r
+  querytype_txt=16;\r
+  querytype_spf=99;\r
+  maxrecursion=50;\r
   maxrrofakind=20;\r
 \r
   retryafter=300000; //microseconds must be less than one second;\r
@@ -170,16 +177,13 @@ procedure setstate_reverse(const binip:tbinip;var state:tdnsstate);
 procedure setstate_failure(var state:tdnsstate);\r
 //procedure setstate_return(const rrp:trrpointer;len:integer;var state:tdnsstate);\r
 \r
+//for custom raw lookups such as TXT, as desired by the user\r
+procedure setstate_custom(const name:string; requesttype:integer; var state:tdnsstate);\r
 \r
 procedure state_process(var state:tdnsstate);\r
 \r
 //function decodename(const packet:tdnspacket;len,start,recursion:integer;var numread:integer):string;\r
 \r
-//presumablly this is exported to allow more secure random functions\r
-//to be substituted?\r
-var randomfunction:function:integer;\r
-\r
-\r
 procedure populatednsserverlist;\r
 procedure cleardnsservercache;\r
 \r
@@ -199,18 +203,20 @@ procedure reportlag(id:integer;lag:integer); //lag should be in microseconds and
 { $endif}\r
 \r
 \r
-{$ifdef linux}{$ifdef ipv6}\r
+{$ifdef ipv6}\r
 function getv6localips:tbiniplist;\r
 procedure initpreferredmode;\r
 \r
 var\r
   preferredmodeinited:boolean;\r
 \r
-{$endif}{$endif}\r
+{$endif}\r
 \r
 var\r
   failurereason:string;\r
 \r
+function getquerytype(s:string):integer;\r
+\r
 implementation\r
 \r
 uses\r
@@ -220,6 +226,24 @@ uses
 \r
   sysutils;\r
 \r
+\r
+\r
+function getquerytype(s:string):integer;\r
+begin\r
+  s := uppercase(s);\r
+  result := 0;\r
+  if (s = 'A') then result := querytype_a else\r
+  if (s = 'CNAME') then result := querytype_cname else\r
+  if (s = 'AAAA') then result := querytype_aaaa else\r
+  if (s = 'PTR') then result := querytype_ptr else\r
+  if (s = 'NS') then result := querytype_ns else\r
+  if (s = 'MX') then result := querytype_mx else\r
+  if (s = 'A6') then result := querytype_a6 else\r
+  if (s = 'TXT') then result := querytype_txt else\r
+  if (s = 'SOA') then result := querytype_soa else\r
+  if (s = 'SPF') then result := querytype_spf;\r
+end;\r
+\r
 function buildrequest(const name:string;var packet:tdnspacket;requesttype:word):integer;\r
 var\r
   a,b:integer;\r
@@ -229,7 +253,8 @@ begin
  { writeln('buildrequest: name: ',name);}\r
   result := 0;\r
   fillchar(packet,sizeof(packet),0);\r
-  if assigned(randomfunction) then packet.id := (randomfunction and $ffff) else packet.id := random(65536);\r
+  packet.id := randominteger($10000);\r
+\r
   packet.flags := htons($0100);\r
   packet.rrcount[0] := htons($0001);\r
 \r
@@ -360,6 +385,14 @@ end;
 \r
 {==============================================================================}\r
 \r
+function getrawfromrr(const rrp:trrpointer;len:integer):string;\r
+begin\r
+  setlength(result,htons(trr(rrp.p^).datalen));\r
+  uniquestring(result);\r
+  move(trr(rrp.p^).data,result[1],length(result));\r
+end;\r
+\r
+\r
 function getipfromrr(const rrp:trrpointer;len:integer):tbinip;\r
 begin\r
   fillchar(result,sizeof(result),0);\r
@@ -391,6 +424,16 @@ begin
     querytype_a{$ifdef ipv6},querytype_aaaa{$endif}: begin\r
       state.resultbin := getipfromrr(rrp,len);\r
     end;\r
+    querytype_txt:begin\r
+      {TXT returns a raw string}\r
+      state.resultstr := copy(getrawfromrr(rrp,len),2,9999);\r
+      fillchar(state.resultbin,sizeof(state.resultbin),0);\r
+    end;\r
+    querytype_mx:begin\r
+      {MX is a name after a 16 bits word}\r
+      state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+12,0,a);\r
+      fillchar(state.resultbin,sizeof(state.resultbin),0);\r
+    end;\r
   else\r
     {other reply types (PTR, MX) return a hostname}\r
     state.resultstr := decodename(state.recvpacket,state.recvpacketlen,taddrint(rrp.p)-taddrint(@state.recvpacket)+10,0,a);\r
@@ -425,6 +468,13 @@ begin
   state.requesttype := querytype_ptr;\r
 end;\r
 \r
+procedure setstate_custom(const name:string; requesttype:integer; var state:tdnsstate);\r
+begin\r
+  setstate_request_init(name,state);\r
+  state.requesttype := requesttype;\r
+end;\r
+\r
+\r
 procedure setstate_failure(var state:tdnsstate);\r
 begin\r
   state.resultstr := '';\r
@@ -701,7 +751,9 @@ end;
 \r
 \r
 \r
-{$ifdef linux}{$ifdef ipv6}\r
+{$ifdef ipv6}\r
+\r
+{$ifdef linux}\r
 function getv6localips:tbiniplist;\r
 var\r
   t:textfile;\r
@@ -728,6 +780,13 @@ begin
   closefile(t);\r
 end;\r
 \r
+{$else}\r
+function getv6localips:tbiniplist;\r
+begin\r
+  result := biniplist_new;\r
+end;\r
+{$endif}\r
+\r
 procedure initpreferredmode;\r
 var\r
   l:tbiniplist;\r
@@ -738,8 +797,9 @@ var
 begin\r
   if preferredmodeinited then exit;\r
   if useaf <> useaf_default then exit;\r
-  useaf := useaf_preferv4;\r
   l := getv6localips;\r
+  if biniplist_getcount(l) = 0 then exit;\r
+  useaf := useaf_preferv4;\r
   ipstrtobin('2000::',ipmask_global);\r
   ipstrtobin('2001::',ipmask_teredo);\r
   ipstrtobin('2002::',ipmask_6to4);\r
@@ -755,7 +815,7 @@ begin
   end;\r
 end;\r
 \r
-{$endif}{$endif}\r
+{$endif}\r
 \r
 \r
 {  quick and dirty description of dns packet structure to aid writing and\r
index b682acf378c2df70d84b2a85bb510c6efb570e56..a7ba7144ee9b14815b0c88449c4cfe2228b99db7 100755 (executable)
@@ -25,6 +25,7 @@ interface
       sockets,\r
       fd_utils,\r
     {$endif}\r
+    lcorernd,\r
     sysutils;\r
 \r
 //convert a name to an IP\r
@@ -54,8 +55,9 @@ const
   defaulttimeout=10000;\r
   const mintimeout=16;\r
 \r
+  toport='53';\r
+\r
 var\r
-  dnssyncserver:string;\r
   id:integer;\r
 \r
   sendquerytime:array[0..numsock-1] of integer;\r
@@ -77,6 +79,7 @@ var
   numsockused:integer;\r
   fd:array[0..numsock-1] of integer;\r
   state:array[0..numsock-1] of tdnsstate;\r
+  toaddr:array[0..numsock-1] of tbinip;\r
 \r
 {$ifdef syncdnscore}\r
 \r
@@ -115,10 +118,15 @@ begin
   result := false;\r
   if len = 0 then exit; {no packet}\r
 \r
-  if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);\r
-  port := '53';\r
+  if overridednsserver <> '' then addr := overridednsserver else addr := getcurrentsystemnameserver(id);\r
+\r
+  {$ifdef ipv6}{$ifdef win32}\r
+  if toaddr[socknum].family = AF_INET6 then if (useaf = 0) then useaf := useaf_preferv6;\r
+  {$endif}{$endif}\r
 \r
-  makeinaddrv(ipstrtobinf(addr),port,inaddr);\r
+  port := toport;\r
+  toaddr[socknum] := ipstrtobinf(addr);\r
+  makeinaddrv(toaddr[socknum],port,inaddr);\r
 \r
   sendto(fd[socknum],packet,len,0,inaddr,inaddrsize(inaddr));\r
   sendquerytime[socknum] := getts;\r
@@ -135,16 +143,17 @@ begin
   //init both sockets smultaneously, always, so they get succesive fd's\r
   if fd[0] > 0 then exit;\r
 \r
-  if dnssyncserver <> '' then addr := dnssyncserver else addr := getcurrentsystemnameserver(id);\r
+  if overridednsserver <> '' then addr := overridednsserver else addr := getcurrentsystemnameserver(id);\r
   //must get the DNS server here so we know to init v4 or v6\r
 \r
-  fillchar(inaddrtemp,sizeof(inaddrtemp),0);\r
   ipstrtobin(addr,biniptemp);\r
-  if biniptemp.family = 0 then biniptemp.family := AF_INET;\r
 \r
-  inaddrtemp.inaddr.family := biniptemp.family;\r
+  if biniptemp.family = AF_INET6 then biniptemp := ipstrtobinf('::') else biniptemp := ipstrtobinf('0.0.0.0');\r
+\r
 \r
   for a := 0 to numsockused-1 do begin\r
+    makeinaddrv(biniptemp,inttostr( 1024 + randominteger(65536 - 1024) ),inaddrtemp);\r
+\r
     fd[a] := Socket(biniptemp.family,SOCK_DGRAM,0);\r
 \r
     If {$ifndef win32}Not{$endif} Bind(fd[a],inAddrtemp,inaddrsize(inaddrtemp)) Then begin\r
@@ -175,6 +184,12 @@ var
   finished:array[0..numsock-1] of boolean;\r
   a,b:integer;\r
 \r
+  Src    : TInetSockAddrV;\r
+  Srcx   : {$ifdef win32}sockaddr_in{$else}TInetSockAddrV{$endif} absolute Src;\r
+  SrcLen : Integer;\r
+  fromip:tbinip;\r
+  fromport:string;\r
+\r
 begin\r
   if timeout < mintimeout then timeout := defaulttimeout;\r
 \r
@@ -242,10 +257,22 @@ begin
         fillchar(state[socknum].recvpacket,sizeof(state[socknum].recvpacket),0);\r
         msectotimeval(lag,(currenttime-sendquerytime[socknum]) and tsmask);\r
 \r
-        if dnssyncserver = '' then reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);\r
-        state[socknum].recvpacketlen := recv(fd[socknum],state[socknum].recvpacket, SizeOf(state[socknum].recvpacket),0);\r
-        state[socknum].parsepacket := true;\r
-        needprocessing[socknum] := true;\r
+        if overridednsserver = '' then reportlag(id,(lag.tv_sec*1000000)+lag.tv_usec);\r
+\r
+        SrcLen := SizeOf(Src);\r
+        state[socknum].recvpacketlen := recvfrom(fd[socknum],state[socknum].recvpacket, SizeOf(state[socknum].recvpacket),0,Srcx,SrcLen);\r
+\r
+        if (state[socknum].recvpacketlen > 0) then begin\r
+          fromip := inaddrvtobinip(Src);\r
+          fromport := inttostr(htons(src.InAddr.port));\r
+          if ((not comparebinip(toaddr[socknum],fromip)) or (fromport <> toport)) then begin\r
+//            writeln('dnssync received from wrong IP:port ',ipbintostr(fromip),'#',fromport);\r
+            state[socknum].recvpacketlen := 0;\r
+          end else begin\r
+            state[socknum].parsepacket := true;\r
+            needprocessing[socknum] := true;\r
+          end;\r
+        end;\r
       end;\r
     end;\r
     if selectresult < 0 then exit;\r
@@ -253,7 +280,7 @@ begin
 \r
       currenttime := getts;\r
 \r
-      if dnssyncserver = '' then reportlag(id,-1);\r
+      if overridednsserver = '' then reportlag(id,-1);\r
       if (currenttime >= endtime) and ((not wrapmode) or (currenttime < starttime)) then begin\r
         exit;\r
       end else begin\r
@@ -307,7 +334,7 @@ begin
   {$endif}\r
   begin\r
   {$ifdef syncdnscore}\r
-    {$ifdef linux}{$ifdef ipv6}initpreferredmode;{$endif}{$endif}\r
+    {$ifdef ipv6}initpreferredmode;{$endif}\r
 \r
     numsockused := 0;\r
 \r
@@ -334,7 +361,7 @@ begin
     end else begin\r
       biniplist_addlist(result,state[0].resultlist);\r
       biniplist_addlist(result,state[1].resultlist);\r
-    {$endif}  \r
+    {$endif}\r
     end;\r
     {$endif}\r
   end;\r
index 73f97adfc2431c4a28a6b89fcffc3649f45a0b58..19eabe46124018816564f0b71d7a575ffcc69adf 100755 (executable)
@@ -20,7 +20,6 @@ type
   //release should only be called from the main thread\r
   tdnswinasync=class(tthread)\r
   private\r
-    ipv6preffered : boolean;\r
     freverse : boolean;\r
     error : integer;\r
     freewhendone : boolean;\r
@@ -32,7 +31,7 @@ type
     name : string;\r
     iplist : tbiniplist;\r
 \r
-    procedure forwardlookup(name:string;ipv6preffered:boolean);\r
+    procedure forwardlookup(name:string);\r
     procedure reverselookup(ip:tbinip);\r
     destructor destroy; override;\r
     procedure release;\r
@@ -281,10 +280,9 @@ begin
   end;\r
 end;\r
 \r
-procedure tdnswinasync.forwardlookup(name:string;ipv6preffered:boolean);\r
+procedure tdnswinasync.forwardlookup(name:string);\r
 begin\r
   self.name := name;\r
-  self.ipv6preffered := ipv6preffered;\r
   freverse := false;\r
   resume;\r
 end;\r
diff --git a/fastmd5.pas b/fastmd5.pas
new file mode 100644 (file)
index 0000000..f0481a9
--- /dev/null
@@ -0,0 +1,297 @@
+{ Copyright (C) 2005 Bas Steendijk and Peter Green\r
+  For conditions of distribution and use, see copyright notice in zlib_license.txt\r
+  Which is included in the package\r
+  ----------------------------------------------------------------------------- }\r
+\r
+unit fastmd5;\r
+\r
+{\r
+pascal implementation of MD5\r
+\r
+written by Bas Steendijk - steendijk@xs4all.nl\r
+\r
+based on RFC1321 - The MD5 Message-Digest Algorithm\r
+\r
+optimized for speed: saved on copying and sub calls in the core routine\r
+\r
+verified on:\r
+- Borland Delphi 3\r
+- Borland Turbo Pascal 7\r
+- Free Pascal 1.0.6 for i386 (on *nix)\r
+- various other versions of freepascal on windows and linux i386\r
+- various other versions of delphi\r
+- free pascal 1.9.5 on powerpc darwin\r
+\r
+this unit is endian portable but is likely to be significantly slower on big endian systems\r
+}\r
+\r
+{$Q-,R-}\r
+\r
+interface\r
+\r
+\r
+\r
+\r
+\r
+type\r
+  Tmd5=array[0..15] of byte;\r
+\r
+{$i uint32.inc}\r
+\r
+type\r
+  dvar=array[0..0] of byte;\r
+  Tmd5state=record\r
+    buf:array[0..63] of byte;\r
+    H:array[0..3] of uint32;\r
+    msglen:longint;\r
+    msglenhi:longint;\r
+  end;\r
+\r
+procedure md5processblock(var h:array of uint32;const data);\r
+\r
+procedure md5init(var state:tmd5state);\r
+procedure md5process(var state:tmd5state;const data;len:longint);\r
+procedure md5finish(var state:tmd5state;var result);\r
+\r
+procedure getmd5(const data;len:longint;var result);\r
+\r
+function md5tostr(const md5:tmd5):string;\r
+\r
+implementation\r
+\r
+function inttohex(val,bits:integer):string;\r
+const\r
+  hexchar:array[0..15] of char='0123456789abcdef';\r
+begin\r
+  inttohex := hexchar[val shr 4]+hexchar[val and $f];\r
+end;\r
+\r
+{$ifdef cpu386}\r
+function rol(w,bits:uint32): uint32; assembler;\r
+asm\r
+  {cpu386 is not defined on freepascal. but fpc assembler is incompatible, uses different code}\r
+  {inline($89/$d1/$d3/$c0);}\r
+  mov   ecx,edx\r
+  rol   eax,cl\r
+end;\r
+{$else}\r
+function rol(w,bits:uint32):uint32;\r
+begin\r
+  rol := (w shl bits) or (w shr (32-bits));\r
+end;\r
+{$endif}\r
+\r
+\r
+{function swapbytes(invalue:uint32):uint32;\r
+var\r
+  inbytes  : array[0..3] of byte absolute invalue;\r
+  outbytes : array[0..3] of byte absolute result;\r
+\r
+\r
+begin\r
+  outbytes[0] := inbytes[3];\r
+  outbytes[1] := inbytes[2];\r
+  outbytes[2] := inbytes[1];\r
+  outbytes[3] := inbytes[0];\r
+end;}\r
+\r
+procedure md5processblock(var h:array of uint32;const data);\r
+const\r
+  S11=7;  S12=12;  S13=17;  S14=22;\r
+  S21=5;  S22=9;   S23=14;  S24=20;\r
+  S31=4;  S32=11;  S33=16;  S34=23;\r
+  S41=6;  S42=10;  S43=15;  S44=21;\r
+\r
+var\r
+  A,B,C,D:uint32;\r
+  w:array[0..63] of byte absolute data;\r
+  x:array[0..15] of uint32 {$ifndef ENDIAN_BIG} absolute data{$endif} ;\r
+  y:array[0..63] of byte absolute x;\r
+  {$ifdef ENDIAN_BIG}counter : integer;{$endif}\r
+begin\r
+  A := h[0];\r
+  B := h[1];\r
+  C := h[2];\r
+  D := h[3];\r
+  {$ifdef ENDIAN_BIG}\r
+    for counter := 0 to 63 do begin\r
+      y[counter] := w[counter xor 3];\r
+    end;\r
+  {$endif}\r
+  a := rol(a + ((b and c) or ((not b) and d)) + x[ 0] + $d76aa478, S11) + b;\r
+  d := rol(d + ((a and b) or ((not a) and c)) + x[ 1] + $e8c7b756, S12) + a;\r
+  c := rol(c + ((d and a) or ((not d) and b)) + x[ 2] + $242070db, S13) + d;\r
+  b := rol(b + ((c and d) or ((not c) and a)) + x[ 3] + $c1bdceee, S14) + c;\r
+  a := rol(a + ((b and c) or ((not b) and d)) + x[ 4] + $f57c0faf, S11) + b;\r
+  d := rol(d + ((a and b) or ((not a) and c)) + x[ 5] + $4787c62a, S12) + a;\r
+  c := rol(c + ((d and a) or ((not d) and b)) + x[ 6] + $a8304613, S13) + d;\r
+  b := rol(b + ((c and d) or ((not c) and a)) + x[ 7] + $fd469501, S14) + c;\r
+  a := rol(a + ((b and c) or ((not b) and d)) + x[ 8] + $698098d8, S11) + b;\r
+  d := rol(d + ((a and b) or ((not a) and c)) + x[ 9] + $8b44f7af, S12) + a;\r
+  c := rol(c + ((d and a) or ((not d) and b)) + x[10] + $ffff5bb1, S13) + d;\r
+  b := rol(b + ((c and d) or ((not c) and a)) + x[11] + $895cd7be, S14) + c;\r
+  a := rol(a + ((b and c) or ((not b) and d)) + x[12] + $6b901122, S11) + b;\r
+  d := rol(d + ((a and b) or ((not a) and c)) + x[13] + $fd987193, S12) + a;\r
+  c := rol(c + ((d and a) or ((not d) and b)) + x[14] + $a679438e, S13) + d;\r
+  b := rol(b + ((c and d) or ((not c) and a)) + x[15] + $49b40821, S14) + c;\r
+\r
+  a := rol(a + ((b and d) or (c and (not d))) + x[ 1] + $f61e2562, S21) + b;\r
+  d := rol(d + ((a and c) or (b and (not c))) + x[ 6] + $c040b340, S22) + a;\r
+  c := rol(c + ((d and b) or (a and (not b))) + x[11] + $265e5a51, S23) + d;\r
+  b := rol(b + ((c and a) or (d and (not a))) + x[ 0] + $e9b6c7aa, S24) + c;\r
+  a := rol(a + ((b and d) or (c and (not d))) + x[ 5] + $d62f105d, S21) + b;\r
+  d := rol(d + ((a and c) or (b and (not c))) + x[10] + $02441453, S22) + a;\r
+  c := rol(c + ((d and b) or (a and (not b))) + x[15] + $d8a1e681, S23) + d;\r
+  b := rol(b + ((c and a) or (d and (not a))) + x[ 4] + $e7d3fbc8, S24) + c;\r
+  a := rol(a + ((b and d) or (c and (not d))) + x[ 9] + $21e1cde6, S21) + b;\r
+  d := rol(d + ((a and c) or (b and (not c))) + x[14] + $c33707d6, S22) + a;\r
+  c := rol(c + ((d and b) or (a and (not b))) + x[ 3] + $f4d50d87, S23) + d;\r
+  b := rol(b + ((c and a) or (d and (not a))) + x[ 8] + $455a14ed, S24) + c;\r
+  a := rol(a + ((b and d) or (c and (not d))) + x[13] + $a9e3e905, S21) + b;\r
+  d := rol(d + ((a and c) or (b and (not c))) + x[ 2] + $fcefa3f8, S22) + a;\r
+  c := rol(c + ((d and b) or (a and (not b))) + x[ 7] + $676f02d9, S23) + d;\r
+  b := rol(b + ((c and a) or (d and (not a))) + x[12] + $8d2a4c8a, S24) + c;\r
+\r
+  a := rol(a + (b xor c xor d) + x[ 5] + $fffa3942, S31) + b;\r
+  d := rol(d + (a xor b xor c) + x[ 8] + $8771f681, S32) + a;\r
+  c := rol(c + (d xor a xor b) + x[11] + $6d9d6122, S33) + d;\r
+  b := rol(b + (c xor d xor a) + x[14] + $fde5380c, S34) + c;\r
+  a := rol(a + (b xor c xor d) + x[ 1] + $a4beea44, S31) + b;\r
+  d := rol(d + (a xor b xor c) + x[ 4] + $4bdecfa9, S32) + a;\r
+  c := rol(c + (d xor a xor b) + x[ 7] + $f6bb4b60, S33) + d;\r
+  b := rol(b + (c xor d xor a) + x[10] + $bebfbc70, S34) + c;\r
+  a := rol(a + (b xor c xor d) + x[13] + $289b7ec6, S31) + b;\r
+  d := rol(d + (a xor b xor c) + x[ 0] + $eaa127fa, S32) + a;\r
+  c := rol(c + (d xor a xor b) + x[ 3] + $d4ef3085, S33) + d;\r
+  b := rol(b + (c xor d xor a) + x[ 6] + $04881d05, S34) + c;\r
+  a := rol(a + (b xor c xor d) + x[ 9] + $d9d4d039, S31) + b;\r
+  d := rol(d + (a xor b xor c) + x[12] + $e6db99e5, S32) + a;\r
+  c := rol(c + (d xor a xor b) + x[15] + $1fa27cf8, S33) + d;\r
+  b := rol(b + (c xor d xor a) + x[ 2] + $c4ac5665, S34) + c;\r
+\r
+  a := rol(a + (c xor (b or (not d))) + x[ 0] + $f4292244, S41) + b;\r
+  d := rol(d + (b xor (a or (not c))) + x[ 7] + $432aff97, S42) + a;\r
+  c := rol(c + (a xor (d or (not b))) + x[14] + $ab9423a7, S43) + d;\r
+  b := rol(b + (d xor (c or (not a))) + x[ 5] + $fc93a039, S44) + c;\r
+  a := rol(a + (c xor (b or (not d))) + x[12] + $655b59c3, S41) + b;\r
+  d := rol(d + (b xor (a or (not c))) + x[ 3] + $8f0ccc92, S42) + a;\r
+  c := rol(c + (a xor (d or (not b))) + x[10] + $ffeff47d, S43) + d;\r
+  b := rol(b + (d xor (c or (not a))) + x[ 1] + $85845dd1, S44) + c;\r
+  a := rol(a + (c xor (b or (not d))) + x[ 8] + $6fa87e4f, S41) + b;\r
+  d := rol(d + (b xor (a or (not c))) + x[15] + $fe2ce6e0, S42) + a;\r
+  c := rol(c + (a xor (d or (not b))) + x[ 6] + $a3014314, S43) + d;\r
+  b := rol(b + (d xor (c or (not a))) + x[13] + $4e0811a1, S44) + c;\r
+  a := rol(a + (c xor (b or (not d))) + x[ 4] + $f7537e82, S41) + b;\r
+  d := rol(d + (b xor (a or (not c))) + x[11] + $bd3af235, S42) + a;\r
+  c := rol(c + (a xor (d or (not b))) + x[ 2] + $2ad7d2bb, S43) + d;\r
+  b := rol(b + (d xor (c or (not a))) + x[ 9] + $eb86d391, S44) + c;\r
+\r
+  inc(h[0],A);\r
+  inc(h[1],B);\r
+  inc(h[2],C);\r
+  inc(h[3],D);\r
+end;\r
+\r
+procedure md5init(var state:tmd5state);\r
+begin\r
+  state.h[0] := $67452301;\r
+  state.h[1] := $EFCDAB89;\r
+  state.h[2] := $98BADCFE;\r
+  state.h[3] := $10325476;\r
+  state.msglen := 0;\r
+  state.msglenhi := 0;\r
+end;\r
+\r
+procedure md5process(var state:tmd5state;const data;len:longint);\r
+var\r
+  a,b:longint;\r
+  ofs:longint;\r
+  p:dvar absolute data;\r
+begin\r
+  b := state.msglen and 63;\r
+\r
+  inc(state.msglen,len);\r
+  while (state.msglen > $20000000) do begin\r
+    dec(state.msglen,$20000000);\r
+    inc(state.msglenhi);\r
+  end;\r
+  ofs := 0;\r
+  if b > 0 then begin\r
+    a := 64-b;\r
+    if a > len then a := len;\r
+    move(p[0],state.buf[b],a);\r
+    inc(ofs,a);\r
+    dec(len,a);\r
+    if b+a = 64 then md5processblock(state.h,state.buf);\r
+    if len = 0 then exit;\r
+  end;\r
+  while len >= 64 do begin\r
+    md5processblock(state.h,p[ofs]);\r
+    inc(ofs,64);\r
+    dec(len,64);\r
+  end;\r
+  if len > 0 then move(p[ofs],state.buf[0],len);\r
+end;\r
+\r
+procedure md5finish(var state:tmd5state;var result);\r
+var\r
+  b       :integer;\r
+  {$ifdef endian_big}\r
+    h       :tmd5 absolute state.h;\r
+    r       :tmd5 absolute result;\r
+    counter :integer ;\r
+  {$endif}\r
+begin\r
+  b := state.msglen and 63;\r
+  state.buf[b] := $80;\r
+  if b >= 56 then begin\r
+    {-- for a := b+1 to 63 do state.buf[a] := 0; }\r
+    fillchar(state.buf[b+1],63-b,0);\r
+    md5processblock(state.h,state.buf);\r
+    fillchar(state.buf,56,0);\r
+  end else begin\r
+    {-- for a := b+1 to 55 do state.buf[a] := 0; }\r
+    fillchar(state.buf[b+1],55-b,0);\r
+  end;\r
+  state.msglen := state.msglen shl 3;\r
+\r
+  state.buf[56] := state.msglen;\r
+  state.buf[57] := state.msglen shr 8;\r
+  state.buf[58] := state.msglen shr 16;\r
+  state.buf[59] := state.msglen shr 24;\r
+  state.buf[60] := state.msglenhi;\r
+  state.buf[61] := state.msglenhi shr 8;\r
+  state.buf[62] := state.msglenhi shr 16;\r
+  state.buf[63] := state.msglenhi shr 24;\r
+\r
+  md5processblock(state.h,state.buf);\r
+  {$ifdef ENDIAN_BIG}\r
+    for counter := 0 to 15 do begin\r
+      r[counter] := h[counter xor 3];\r
+    end;\r
+  {$else} \r
+    move(state.h,result,16);\r
+  {$endif}\r
+  fillchar(state,sizeof(state),0);\r
+end;\r
+\r
+procedure getmd5(const data;len:longint;var result);\r
+var\r
+  t:tmd5state;\r
+begin\r
+  md5init(t);\r
+  md5process(t,data,len);\r
+  md5finish(t,result);\r
+end;\r
+\r
+function md5tostr(const md5:tmd5):string;\r
+var\r
+  a:integer;\r
+  s:string;\r
+begin\r
+  s := '';\r
+  for a := 0 to 15 do s := s + inttohex(md5[a],2);\r
+  md5tostr := s;\r
+end;\r
+\r
+end.\r
index 27b6e825afb445be8571e70eb1f4ddba6e77d347..cda6ec7732967d5c1b8b480fdcf8a5d4aae298c7 100644 (file)
@@ -12,6 +12,7 @@ to disable, undefine it here, or define "noipv6" in the app}
 {$define ipv6}\r
 {$endif}\r
 \r
+{-------------------------------------------------------------------------------------}\r
 {there are 2 ways to use DNS in lcore: dnscore, which an entire built in DNS client, and getaddrinfo.\r
 dnscore is always included on *nix to avoid libc dependency problems, but getaddrinfo is used on windows.\r
 when getaddrinfo is used, there is no reason to include dnscore, and it increases the exe size,\r
@@ -19,6 +20,14 @@ unless you want to use custom nameserver addresses. enable this setting to alway
 \r
 {-$define syncdnscore}\r
 \r
+{-------------------------------------------------------------------------------------}\r
+{lcore contains a built in general purpose secure random number generator, which is used elsewhere in lcore, for\r
+example by the DNS resolver. the used random function can be hooked to point to one's own RNG as desired.\r
+it is then also possible to not include the built in RNG in the exe, which reduces code size}\r
+\r
+{-$define nolcorernd}\r
+\r
+{-------------------------------------------------------------------------------------}\r
 {on windows up to XP, listening on ipv6 will not listen on ipv4, while on other platforms it does, \r
 so a single listener cant get all connections for a port number, only those for one address family.\r
 also it means a portable app would gave to deal with inconsistent behavior.\r
@@ -27,3 +36,5 @@ enable this option to simulate the behavior of listening on both v4 and v6}
 {$ifdef win32}{$ifdef ipv6}\r
 {$define secondlistener}\r
 {$endif}{$endif}\r
+\r
+{-------------------------------------------------------------------------------------}
\ No newline at end of file
diff --git a/lcorernd.pas b/lcorernd.pas
new file mode 100644 (file)
index 0000000..006f6ce
--- /dev/null
@@ -0,0 +1,427 @@
+{ Copyright (C) 2005 Bas Steendijk and Peter Green\r
+  For conditions of distribution and use, see copyright notice in zlib_license.txt\r
+  which is included in the package\r
+  ----------------------------------------------------------------------------- }\r
+\r
+unit lcorernd;\r
+\r
+interface\r
+\r
+{$include lcoreconfig.inc}\r
+\r
+{\r
+written by Bas Steendijk (beware)\r
+\r
+the aim of this unit is to provide randomness in a consistent way, using OS specific methods for seeding\r
+\r
+this unit uses MD5 for performance and code size, but it is made so it is easy to use a different hash,\r
+as long as it is atleat 128 bits, and a multiple of the "word size" (32 bits)\r
+\r
+goals:\r
+\r
+- for the code to be:\r
+ - relatively simple and small\r
+ - reasonably fast\r
+\r
+- for the numbers to be\r
+ - random: pass diehard and similar tests\r
+ - unique: generate UUID's\r
+ - secure: difficult for a remote attacker to guess the internal state, even\r
+   when given some output\r
+\r
+typical intended uses:\r
+ - anything that needs random numbers without extreme demands on security or\r
+   speed should be able to use this\r
+ - seeding other (faster) RNG's\r
+ - generation of passwords, UUID's, cookies, and session keys\r
+ - randomizing protocol fields to protect against spoofing attacks\r
+ - randomness for games\r
+\r
+this is not intended to be directly used for:\r
+- high securirity purposes (generating RSA root keys etc)\r
+- needing random numbers at very high rates (disk wiping, some simulations, etc)\r
+\r
+performance:\r
+- 24 MB/s on 2.2 GHz athlon64 core on windows XP 32 bits\r
+- 6.4 MB/s on 1 GHz p3 on linux\r
+\r
+exe size:\r
+- fpc 2.2, linux: fastmd5: 12 kb; lcorernd: 6 kb.\r
+- delphi 6: fastmd5: 3 kb; lcorernd: 2 kb\r
+\r
+reasoning behind the security of this RNG:\r
+\r
+- seeding:\r
+1: i assume that any attacker has no local access to the machine. if one gained\r
+  this, then there are more seriousness weaknesses to consider.\r
+2: i attempt to use enough seeding to be difficult to guess.\r
+  on windows: GUID, various readouts of hi res timestamps, heap stats, cursor\r
+  position\r
+  on *nix: i assume /dev/(u)random output is secure and difficult to guess. if\r
+  it misses, i use /dev/wtmp, which typically has as lot of entropy in it. i also use hi res timestamps.\r
+3: on a state compromise, one can easily get up to the hash size worth of previous output, beyond that one has\r
+  to invert the hash operation.\r
+\r
+- mixing/expansion: a secure hash random walk on a buffer with a constant secret and a changing exposed part,\r
+  the big secret part serves to make it difficult for an attacker to predict next and previous output.\r
+  the secret part is changed during a reseed.\r
+\r
+\r
+                                       OS randomness\r
+                                             v\r
+                              <wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww>\r
+ ____________________________  ________________________________________________\r
+[            pool            ][                    seed                        ]\r
+[hashsize][hashsize][hashsize]\r
+          <rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr>\r
+                bighash()             seeding\r
+                   v\r
+          <wwwwwwwwwwwwwwwwww>\r
+<rrrrrrrrrrrrrrrrrrrrrrrrrrrr>\r
+  hash()                            random walk\r
+    v\r
+<wwwwwwww>\r
+[ output ][      secret      ]\r
+\r
+\r
+this needs testing on platforms other than i386\r
+\r
+\r
+these routines are called by everything else in lcore, and if the app coder desires, by the app.\r
+because one may want to use their own random number source, the PRNG here can be excluded from linking,\r
+and the routines here can be hooked.\r
+}\r
+\r
+{$include uint32.inc}\r
+\r
+{return a dword with 32 random bits}\r
+type\r
+  wordtype=uint32;\r
+\r
+var\r
+  randomdword:function:wordtype;\r
+\r
+{fill a buffer with random bytes}\r
+procedure fillrandom(var buf;length:integer);\r
+\r
+{generate an integer of 0 <= N < i}\r
+function randominteger(i:longint):longint;\r
+\r
+{generate an integer with the lowest b bits being random}\r
+function randombits(b:integer):longint;\r
+\r
+{generate a version 4 random uuid}\r
+function generate_uuid:string;\r
+\r
+{$ifndef nolcorernd}\r
+\r
+{call this to mix seeding into the pool. is normally done automatically and does not have to be called\r
+but can be done if one desires more security, for example for key generation}\r
+procedure seedpool;\r
+\r
+{get some raw OS specific randomness. the output is not mixed so it should not be used directly as random numbers}\r
+function collect_seeding(var output;const bufsize:integer):integer;\r
+\r
+function internalrandomdword:wordtype;\r
+\r
+var\r
+  reseedinterval:integer=64;\r
+{$endif}\r
+\r
+implementation\r
+\r
+{$ifndef nolcorernd}\r
+uses\r
+  {$ifdef win32}windows,activex,types,{$endif}\r
+  {$ifdef unix}baseunix,unix,unixutil,{$endif}\r
+  fastmd5,sysutils;\r
+\r
+{$ifdef unix}{$include unixstuff.inc}{$endif}\r
+\r
+type\r
+  {hashtype must be array of bytes}\r
+  hashtype=tmd5;\r
+\r
+const\r
+  wordsizeshift=2;\r
+  wordsize=1 shl wordsizeshift;\r
+\r
+  {$if (wordsize <> sizeof(wordtype))}'wordsizeshift must be setcorrectly'{$ifend}\r
+\r
+  hashsize=sizeof(hashtype);\r
+  halfhashsize=hashsize div 2;\r
+  hashdwords=hashsize div wordsize;\r
+  pooldwords=3*hashdwords;\r
+  seeddwords=32;\r
+  hashpasssize=48; {this number has to be small enough that hashing this size uses only one block transform}\r
+\r
+var\r
+  {the seed part of this buffer must be atleast as big as the OS seed (windows: 104 bytes, unix: 36 bytes)}\r
+  pool:array[0..(pooldwords+seeddwords-1)] of wordtype;\r
+  reseedcountdown:integer;\r
+\r
+{$ifdef win32}\r
+function collect_seeding(var output;const bufsize:integer):integer;\r
+var\r
+  l:packed record\r
+    guid:array[0..3] of longint;\r
+    qpcbuf:array[0..1] of longint;\r
+    rdtscbuf:array[0..1] of longint;\r
+    systemtimebuf:array[0..3] of longint;\r
+    pid:longint;\r
+    tid:longint;\r
+    cursor:tpoint;\r
+    hs:theapstatus;\r
+  end absolute output;\r
+  rdtsc_0,rdtsc_1:integer;\r
+begin\r
+  result := 0;\r
+  if (bufsize < sizeof(l)) then exit;\r
+  result := sizeof(l);\r
+  {PID}\r
+  l.pid := GetCurrentProcessId;\r
+  l.tid := GetCurrentThreadId;\r
+\r
+  {COCREATEGUID}\r
+  cocreateguid(tguid(l.guid));\r
+\r
+  {QUERYPERFORMANCECOUNTER}\r
+  queryperformancecounter(tlargeinteger(l.qpcbuf));\r
+\r
+  {RDTSC}\r
+  {$ifdef cpu386}\r
+  asm\r
+    db $0F; db $31\r
+    mov rdtsc_0,eax\r
+    mov rdtsc_1,edx\r
+  end;\r
+  l.rdtscbuf[0] := rdtsc_0;\r
+  l.rdtscbuf[1] := rdtsc_1;\r
+  {$endif}\r
+  {GETSYSTEMTIME}\r
+  getsystemtime(tsystemtime(l.systemtimebuf));\r
+\r
+  {cursor position}\r
+  getcursorpos(l.cursor);\r
+\r
+  l.hs := getheapstatus;\r
+end;\r
+{$endif}\r
+\r
+{$ifdef unix}\r
+\r
+var\r
+  wtmpinited:boolean;\r
+  wtmpcached:hashtype;\r
+\r
+procedure wtmphash;\r
+var\r
+  f:file;\r
+  buf:array[0..4095] of byte;\r
+  numread:integer;\r
+  state:tmd5state;\r
+begin\r
+  if wtmpinited then exit;\r
+\r
+  assignfile(f,'/var/log/wtmp');\r
+  filemode := 0;\r
+  {$i-}reset(f,1);{$i+}\r
+  if (ioresult <> 0) then exit;\r
+  md5init(state);\r
+  while not eof(f) do begin\r
+    blockread(f,buf,sizeof(buf),numread);\r
+    md5process(state,buf,numread);\r
+  end;\r
+  closefile(f);\r
+  md5finish(state,wtmpcached);\r
+  wtmpinited := true;\r
+end;\r
+\r
+\r
+function collect_seeding(var output;const bufsize:integer):integer;\r
+var\r
+  f:file;\r
+  a:integer;\r
+  l:packed record\r
+    devrnd:array[0..3] of integer;\r
+    rdtscbuf:array[0..1] of integer;\r
+    tv:ttimeval;\r
+    pid:integer;\r
+  end absolute output;\r
+  rdtsc_0,rdtsc_1:integer;\r
+\r
+begin\r
+  result := 0;\r
+  if (bufsize < sizeof(l)) then exit;\r
+  result := sizeof(l);\r
+\r
+  {/DEV/URANDOM}\r
+  a := 1;\r
+  assignfile(f,'/dev/urandom');\r
+  filemode := 0;\r
+  {$i-}reset(f,1);{$i+}\r
+  a := ioresult;\r
+  if (a <> 0) then begin\r
+    assignfile(f,'/dev/random');\r
+    {$i-}reset(f,1);{$i+}\r
+    a := ioresult;\r
+  end;\r
+  if (a = 0) then begin\r
+    blockread(f,l.devrnd,sizeof(l.devrnd));\r
+    closefile(f);\r
+  end else begin\r
+    {the OS we are on has no /dev/random or /dev/urandom, get a hash from /var/log/wtmp}\r
+    wtmphash;\r
+    move(wtmpcached,l.devrnd,sizeof(l.devrnd));\r
+  end;\r
+  {get more randomness in case there's no /dev/random}\r
+  {$ifdef cpu386}{$ASMMODE intel}\r
+  asm\r
+    db $0F; db $31\r
+    mov rdtsc_0,eax\r
+    mov rdtsc_1,edx\r
+  end;\r
+  l.rdtscbuf[0] := rdtsc_0;\r
+  l.rdtscbuf[1] := rdtsc_1;\r
+  {$endif}\r
+\r
+  gettimeofday(l.tv);\r
+  l.pid := getpid;\r
+end;\r
+{$endif}\r
+\r
+{this produces a hash which is twice the native hash size (32 bytes for MD5)}\r
+procedure bighash(const input;len:integer;var output);\r
+var\r
+  inarr:array[0..65535] of byte absolute input;\r
+  outarr:array[0..65535] of byte absolute output;\r
+\r
+  h1,h2,h3,h4:hashtype;\r
+  a:integer;\r
+begin\r
+  a := len div 2;\r
+  {first hash round}\r
+  getmd5(inarr[0],a,h1);\r
+  getmd5(inarr[a],len-a,h2);\r
+\r
+  move(h1[0],h3[0],halfhashsize);\r
+  move(h2[0],h3[halfhashsize],halfhashsize);\r
+  move(h1[halfhashsize],h4[0],halfhashsize);\r
+  move(h2[halfhashsize],h4[halfhashsize],halfhashsize);\r
+\r
+  getmd5(h3,hashsize,outarr[0]);\r
+  getmd5(h4,hashsize,outarr[hashsize]);\r
+end;\r
+\r
+procedure seedpool;\r
+var\r
+  a:integer;\r
+begin\r
+  a := collect_seeding(pool[pooldwords],seeddwords*wordsize);\r
+  if (a = 0) then halt;\r
+  bighash(pool[hashdwords],(2*hashsize)+a,pool[hashdwords]);\r
+  getmd5(pool[0],hashpasssize,pool[0]);\r
+end;\r
+\r
+function internalrandomdword;\r
+begin\r
+  if (reseedcountdown <= 0) then begin\r
+    seedpool;\r
+    reseedcountdown := reseedinterval * hashdwords;\r
+  end else if ((reseedcountdown mod hashdwords) = 0) then begin;\r
+    getmd5(pool[0],hashpasssize,pool[0]);\r
+  end;\r
+  dec(reseedcountdown);\r
+\r
+  result := pool[reseedcountdown mod hashdwords];\r
+end;\r
+{$endif}\r
+\r
+procedure fillrandom(var buf;length:integer);\r
+var\r
+  a,b:integer;\r
+  buf_:array[0..16383] of uint32 absolute buf;\r
+\r
+begin\r
+  b := 0;\r
+  for a := (length shr wordsizeshift)-1 downto 0 do begin\r
+    buf_[b] := randomdword;\r
+    inc(b);\r
+  end;\r
+  length := length and (wordsize-1);\r
+  if length <> 0 then begin\r
+    a := randomdword;\r
+    move(a,buf_[b],length);\r
+  end;\r
+end;\r
+\r
+const\r
+  wordsizebits=32;\r
+\r
+function randombits(b:integer):longint;\r
+begin\r
+  result := randomdword;\r
+  result := result and (-1 shr (wordsizebits-b));\r
+  if (b = 0) then result := 0;\r
+end;\r
+\r
+function randominteger(i:longint):longint;\r
+var\r
+  a,b:integer;\r
+  j:integer;\r
+begin\r
+  //bitscounter := bitscounter + numofbitsininteger(i);\r
+  if (i = 0) then begin\r
+    result := 0;\r
+    exit;\r
+  end;\r
+  {find number of bits needed}\r
+  j := i-1;\r
+  if (j < 0) then begin\r
+    result := randombits(wordsizebits);\r
+    exit\r
+  end else if (j >= (1 shl (wordsizebits-2))) then begin\r
+    b := wordsizebits-1\r
+  end else begin\r
+    b := -1;\r
+    for a := 0 to (wordsizebits-2) do begin\r
+      if j < 1 shl a then begin\r
+        b := a;\r
+        break;\r
+      end;\r
+    end;\r
+  end;\r
+  repeat\r
+    result := randombits(b);\r
+  until result < i;\r
+end;\r
+\r
+const\r
+  ch:array[0..15] of char='0123456789abcdef';\r
+\r
+function generate_uuid:string;\r
+var\r
+  buf:array[0..7] of word;\r
+function inttohex(w:word):string;\r
+begin\r
+  result := ch[w shr 12] + ch[(w shr 8) and $f] + ch[(w shr 4) and $f] + ch[w and $f];\r
+end;\r
+begin\r
+  fillrandom(buf,sizeof(buf));\r
+\r
+  {uuid version 4}\r
+  buf[3] := (buf[3] and $fff) or $4000;\r
+\r
+  {uuid version 4}\r
+  buf[4] := (buf[4] and $3fff) or $8000;\r
+\r
+  result := inttohex(buf[0]) + inttohex(buf[1]) + '-' + inttohex(buf[2]) +'-'+ inttohex(buf[3]) + '-' + inttohex(buf[4])\r
+  + '-' + inttohex(buf[5]) + inttohex(buf[6]) + inttohex(buf[7]);\r
+end;\r
+\r
+{$ifndef nolcorernd}\r
+initialization randomdword := @internalrandomdword;\r
+{$endif}\r
+\r
+end.\r
+\r
index 0d99f6ad900c4dd5b0d6bf63d616ed7f81a69334..16134ee6a73fc3a323ca19521f14de5326c9d617 100755 (executable)
@@ -31,6 +31,8 @@ var
 function getfdsrmaster : fdset; {$ifdef useinline}inline;{$endif}\r
 function getfdswmaster : fdset; {$ifdef useinline}inline;{$endif}\r
 \r
+procedure lcoreinit;\r
+\r
 implementation\r
 uses\r
   lcore,sysutils,\r
@@ -392,9 +394,13 @@ begin
   fdreverse[fd] := reverseto;\r
 end;\r
 \r
+var\r
+  inited:boolean;\r
 \r
-\r
+procedure lcoreinit;\r
 begin\r
+  if inited then exit;\r
+  inited := true;\r
   eventcore := tselecteventcore.create;\r
 \r
   absoloutemaxs := absoloutemaxs_select;\r
@@ -402,4 +408,6 @@ begin
   maxs := 0;\r
   fd_zero(fdsrmaster);\r
   fd_zero(fdswmaster);\r
+end;\r
+\r
 end.\r
index b64797ae174c9f2959f0fb776a2be77a04ee481c..622c92ea82ad1bec51d389bc89e119c1baf702cb 100755 (executable)
@@ -2,8 +2,10 @@ unit lcorewsaasyncselect;
 \r
 interface\r
 \r
+procedure lcoreinit;\r
 \r
 implementation\r
+\r
 uses wcore,lcore,bsearchtree,sysutils,windows,winsock,pgtypes,messages,classes,lsocket;\r
 type\r
   twineventcore=class(teventcore)\r
@@ -197,7 +199,12 @@ var
                                  lpszClassName : 'lcoreClass');\r
   GInitData: TWSAData;\r
 \r
+var\r
+  inited:boolean;\r
+procedure lcoreinit;\r
 begin\r
+  if (inited) then exit;\r
+\r
   eventcore := twineventcore.create;\r
   if Windows.RegisterClass(MyWindowClass) = 0 then halt;\r
   //writeln('about to create lcore handle, hinstance=',hinstance);\r
@@ -216,6 +223,11 @@ begin
   onaddtask := winaddtask;\r
   timerwrapperinterface := twintimerwrapperinterface.create(nil);\r
 \r
-  WSAStartup($200, GInitData);\r
+  WSAStartup(2, GInitData);\r
   absoloutemaxs := maxlongint;\r
+\r
+\r
+  inited := true;\r
+end;\r
+\r
 end.\r
index 898e983b66483974597b07b7fa47c9aa25a4f7f0..2eb4dbdb711bfeab85d5375ab0272006093369a0 100755 (executable)
@@ -299,7 +299,7 @@ begin
       If Bind(fdhandlein,inaddrtempx,inaddrtempsize)<> {$ifdef win32}0{$else}true{$endif} Then begin\r
         state := wsclosed;\r
         lasterror := {$ifdef win32}getlasterror{$else}socketerror{$endif};\r
-        raise ESocketException.create('unable to bind, error '+inttostr(lasterror));\r
+        raise ESocketException.create('unable to bind on address '+localaddr+'#'+localport+', error '+inttostr(lasterror));\r
       end;\r
       state := wsbound;\r
     end;\r
@@ -477,7 +477,10 @@ end;
 \r
 function tlsocket.receivefrom(data:pointer;len:integer;var src:TInetSockAddrV;var srclen:integer):integer;\r
 var\r
-  srcx : {$ifdef win32}winsock.TSockAddr{$else}TInetSockAddrV{$endif} absolute src;\r
+  tempsrc:TInetSockAddrV;\r
+  tempsrclen:integer;\r
+  srcx : {$ifdef win32}winsock.TSockAddr{$else}TInetSockAddrV{$endif} absolute tempsrc;\r
+  biniptemp:tbinip;\r
 begin\r
   {$ifdef secondlistener}\r
   if assigned(secondlistener) then if lastsessionfromsecond then begin\r
@@ -486,7 +489,19 @@ begin
     exit;\r
   end;\r
   {$endif}\r
-  result := {$ifdef win32}winsock{$else}sockets{$endif}.recvfrom(self.fdhandlein,data^,len,0,srcx,srclen);\r
+  tempsrclen := sizeof(tempsrc);\r
+  result := {$ifdef win32}winsock{$else}sockets{$endif}.recvfrom(self.fdhandlein,data^,len,0,srcx,tempsrclen);\r
+\r
+  {$ifdef ipv6}\r
+  biniptemp := inaddrvtobinip(tempsrc);\r
+  if needconverttov4(biniptemp) then begin\r
+    converttov4(biniptemp);\r
+    tempsrclen := makeinaddrv(biniptemp,inttostr(ntohs(tempsrc.InAddr.port)),tempsrc);\r
+  end;\r
+  {$endif}\r
+\r
+  move(tempsrc,src,srclen);\r
+  srclen := tempsrclen;\r
 end;\r
 \r
 procedure tlsocket.connectionfailedhandler(error:word);\r
@@ -603,15 +618,7 @@ begin
   {$else}\r
     sockets.getsocketname(self.fdhandlein,addr,i);\r
   {$endif}\r
-  binip.family := addr.inaddr.family;\r
-  {$ifdef ipv6}\r
-  if addr.inaddr6.sin6_family = AF_INET6 then begin\r
-    binip.ip6 := addr.inaddr6.sin6_addr;\r
-  end else\r
-  {$endif}\r
-  begin\r
-    binip.ip := addr.inaddr.addr;\r
-  end;\r
+  binip := inaddrvtobinip(addr);\r
   converttov4(binip);\r
 end;\r
 \r
@@ -628,15 +635,7 @@ begin
     sockets.getpeername(self.fdhandlein,addr,i);\r
   {$endif}\r
 \r
-  binip.family := addr.inaddr.family;\r
-  {$ifdef ipv6}\r
-  if addr.inaddr6.sin6_family = AF_INET6 then begin\r
-    binip.ip6 := addr.inaddr6.sin6_addr;\r
-  end else\r
-  {$endif}\r
-  begin\r
-    binip.ip := addr.inaddr.addr;\r
-  end;\r
+  binip := inaddrvtobinip(addr);\r
   converttov4(binip);\r
 end;\r
 \r
index b1e27b0afc8f4c516e83f1c44f381d15ed3e35ac..175f295e7424f0a7f06e893398414c82469922e4 100644 (file)
--- a/todo.txt
+++ b/todo.txt
@@ -1,4 +1 @@
-* add multilistener support so that a single tlsocket can be used for both\r
-ipv4 and ipv6 on windows XP\r
-* fixup dnsasync to support multi-ip responses\r
 * fixup dnsasync to perform retries and use multiple dns servers
\ No newline at end of file