* exec.c (expand_args): Remove loop left over from earlier implementation.
[gnupg.git] / cipher / rndw32.c
1 /* rndw32.c  -  W32 entropy gatherer
2  *      Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3  *      Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  *
21  *************************************************************************
22  * The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann.
23  * Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this
24  * copyright notice:
25  *
26  * This module is part of the cryptlib continuously seeded pseudorandom
27  * number generator.  For usage conditions, see lib_rand.c
28  *
29  * [Here is the notice from lib_rand.c, which is now called dev_sys.c]
30  *
31  * This module and the misc/rnd*.c modules represent the cryptlib
32  * continuously seeded pseudorandom number generator (CSPRNG) as described in
33  * my 1998 Usenix Security Symposium paper "The generation of random numbers
34  * for cryptographic purposes".
35  *
36  * The CSPRNG code is copyright Peter Gutmann (and various others) 1996,
37  * 1997, 1998, 1999, all rights reserved.  Redistribution of the CSPRNG
38  * modules and use in source and binary forms, with or without modification,
39  * are permitted provided that the following conditions are met:
40  *
41  * 1. Redistributions of source code must retain the above copyright notice
42  *    and this permission notice in its entirety.
43  *
44  * 2. Redistributions in binary form must reproduce the copyright notice in
45  *    the documentation and/or other materials provided with the distribution.
46  *
47  * 3. A copy of any bugfixes or enhancements made must be provided to the
48  *    author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the
49  *    baseline version of the code.
50  *
51  * ALTERNATIVELY, the code may be distributed under the terms of the GNU
52  * General Public License, version 2 or any later version published by the
53  * Free Software Foundation, in which case the provisions of the GNU GPL are
54  * required INSTEAD OF the above restrictions.
55  *
56  * Although not required under the terms of the GPL, it would still be nice if
57  * you could make any changes available to the author to allow a consistent
58  * code base to be maintained
59  *************************************************************************
60  */
61
62 #include <config.h>
63
64 #ifdef USE_RNDW32
65
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <assert.h>
69 #include <errno.h>
70 #include <string.h>
71
72 #include <windows.h>
73 #ifdef __CYGWIN32__
74 # include <winioctl.h>
75 #endif
76
77
78 #include "types.h"
79 #include "util.h"
80 #include "algorithms.h"
81
82 #include "i18n.h"
83
84
85 static int debug_me;
86
87 /*
88  * Definitions which are missing from the current GNU Windows32Api
89  */
90
91 #ifndef TH32CS_SNAPHEAPLIST
92 #define TH32CS_SNAPHEAPLIST 1
93 #define TH32CS_SNAPPROCESS  2
94 #define TH32CS_SNAPTHREAD   4
95 #define TH32CS_SNAPMODULE   8
96 #define TH32CS_SNAPALL      (1|2|4|8)
97 #define TH32CS_INHERIT      0x80000000
98 #endif /*TH32CS_SNAPHEAPLIST*/
99
100 #ifndef IOCTL_DISK_PERFORMANCE
101 #define IOCTL_DISK_PERFORMANCE  0x00070020
102 #endif
103 #ifndef VER_PLATFORM_WIN32_WINDOWS
104 #define VER_PLATFORM_WIN32_WINDOWS 1
105 #endif
106
107 #define SIZEOF_DISK_PERFORMANCE_STRUCT (6*8+5*4+8*2)
108
109
110 typedef struct {
111     DWORD dwSize;
112     DWORD th32ProcessID;
113     DWORD th32HeapID;
114     DWORD dwFlags;
115 } HEAPLIST32;
116
117 typedef struct {
118     DWORD dwSize;
119     HANDLE hHandle;
120     DWORD dwAddress;
121     DWORD dwBlockSize;
122     DWORD dwFlags;
123     DWORD dwLockCount;
124     DWORD dwResvd;
125     DWORD th32ProcessID;
126     DWORD th32HeapID;
127 } HEAPENTRY32;
128
129 typedef struct {
130     DWORD dwSize;
131     DWORD cntUsage;
132     DWORD th32ProcessID;
133     DWORD th32DefaultHeapID;
134     DWORD th32ModuleID;
135     DWORD cntThreads;
136     DWORD th32ParentProcessID;
137     LONG  pcPriClassBase;
138     DWORD dwFlags;
139     char  szExeFile[260];
140 } PROCESSENTRY32;
141
142 typedef struct {
143     DWORD dwSize;
144     DWORD cntUsage;
145     DWORD th32ThreadID;
146     DWORD th32OwnerProcessID;
147     LONG  tpBasePri;
148     LONG  tpDeltaPri;
149     DWORD dwFlags;
150 } THREADENTRY32;
151
152 typedef struct {
153     DWORD dwSize;
154     DWORD th32ModuleID;
155     DWORD th32ProcessID;
156     DWORD GlblcntUsage;
157     DWORD ProccntUsage;
158     BYTE  *modBaseAddr;
159     DWORD modBaseSize;
160     HMODULE hModule;
161     char  szModule[256];
162     char  szExePath[260];
163 } MODULEENTRY32;
164
165
166
167 /* Type definitions for function pointers to call Toolhelp32 functions
168  * used with the windows95 gatherer */
169 typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme);
170 typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte);
171 typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe);
172 typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl);
173 typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID,
174                                    DWORD th32HeapID);
175 typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe);
176 typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID);
177
178 /* Type definitions for function pointers to call NetAPI32 functions */
179 typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
180                                            DWORD dwLevel, DWORD dwOptions,
181                                            LPBYTE * lpBuffer);
182 typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
183 typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
184
185
186 /* When we query the performance counters, we allocate an initial buffer and
187  * then reallocate it as required until RegQueryValueEx() stops returning
188  * ERROR_MORE_DATA.  The following values define the initial buffer size and
189  * step size by which the buffer is increased
190  */
191 #define PERFORMANCE_BUFFER_SIZE         65536   /* Start at 64K */
192 #define PERFORMANCE_BUFFER_STEP         16384   /* Step by 16K */
193
194
195 static void
196 slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester )
197 {
198     static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
199     static MODULEWALK pModule32First = NULL;
200     static MODULEWALK pModule32Next = NULL;
201     static PROCESSWALK pProcess32First = NULL;
202     static PROCESSWALK pProcess32Next = NULL;
203     static THREADWALK pThread32First = NULL;
204     static THREADWALK pThread32Next = NULL;
205     static HEAPLISTWALK pHeap32ListFirst = NULL;
206     static HEAPLISTWALK pHeap32ListNext = NULL;
207     static HEAPFIRST pHeap32First = NULL;
208     static HEAPNEXT pHeap32Next = NULL;
209     HANDLE hSnapshot;
210
211
212     /* initialize the Toolhelp32 function pointers */
213     if ( !pCreateToolhelp32Snapshot ) {
214         HANDLE hKernel;
215
216         if ( debug_me )
217             log_debug ("rndw32#slow_gatherer_95: init toolkit\n" );
218
219         /* Obtain the module handle of the kernel to retrieve the addresses
220          * of the Toolhelp32 functions */
221         if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) {
222             g10_log_fatal ( "rndw32: can't get module handle\n" );
223         }
224
225         /* Now get pointers to the functions */
226         pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel,
227                                                   "CreateToolhelp32Snapshot");
228         pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First");
229         pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next");
230         pProcess32First = (PROCESSWALK) GetProcAddress (hKernel,
231                                                         "Process32First");
232         pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel,
233                                                        "Process32Next");
234         pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First");
235         pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next");
236         pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel,
237                                                           "Heap32ListFirst");
238         pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel,
239                                                          "Heap32ListNext");
240         pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First");
241         pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next");
242
243         if (    !pCreateToolhelp32Snapshot
244              || !pModule32First || !pModule32Next
245              || !pProcess32First || !pProcess32Next
246              || !pThread32First  || !pThread32Next
247              || !pHeap32ListFirst || !pHeap32ListNext
248              || !pHeap32First     || !pHeap32Next  ) {
249             g10_log_fatal ( "rndw32: failed to get a toolhep function\n" );
250         }
251     }
252
253     /* Take a snapshot of everything we can get to which is currently
254      *  in the system */
255     if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) {
256         g10_log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" );
257     }
258
259     /* Walk through the local heap */
260     {   HEAPLIST32 hl32;
261         hl32.dwSize = sizeof (HEAPLIST32);
262         if (pHeap32ListFirst (hSnapshot, &hl32)) {
263             if ( debug_me )
264                 log_debug ("rndw32#slow_gatherer_95: walk heap\n" );
265             do {
266                 HEAPENTRY32 he32;
267
268                 /* First add the information from the basic Heaplist32 struct */
269                 (*add) ( &hl32, sizeof (hl32), requester );
270
271                 /* Now walk through the heap blocks getting information
272                  * on each of them */
273                 he32.dwSize = sizeof (HEAPENTRY32);
274                 if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){
275                     do {
276                         (*add) ( &he32, sizeof (he32), requester );
277                     } while (pHeap32Next (&he32));
278                 }
279             } while (pHeap32ListNext (hSnapshot, &hl32));
280         }
281     }
282
283
284     /* Walk through all processes */
285     {   PROCESSENTRY32 pe32;
286         pe32.dwSize = sizeof (PROCESSENTRY32);
287         if (pProcess32First (hSnapshot, &pe32)) {
288             if ( debug_me )
289                 log_debug ("rndw32#slow_gatherer_95: walk processes\n" );
290             do {
291                 (*add) ( &pe32, sizeof (pe32), requester );
292             } while (pProcess32Next (hSnapshot, &pe32));
293         }
294     }
295
296     /* Walk through all threads */
297     {   THREADENTRY32 te32;
298         te32.dwSize = sizeof (THREADENTRY32);
299         if (pThread32First (hSnapshot, &te32)) {
300             if ( debug_me )
301                 log_debug ("rndw32#slow_gatherer_95: walk threads\n" );
302             do {
303                 (*add) ( &te32, sizeof (te32), requester );
304             } while (pThread32Next (hSnapshot, &te32));
305         }
306     }
307
308     /* Walk through all modules associated with the process */
309     {   MODULEENTRY32 me32;
310         me32.dwSize = sizeof (MODULEENTRY32);
311         if (pModule32First (hSnapshot, &me32)) {
312             if ( debug_me )
313                 log_debug ("rndw32#slow_gatherer_95: walk modules\n" );
314             do {
315                 (*add) ( &me32, sizeof (me32), requester );
316             } while (pModule32Next (hSnapshot, &me32));
317         }
318     }
319
320     CloseHandle (hSnapshot);
321 }
322
323
324
325 static void
326 slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester )
327 {
328     static int is_initialized = 0;
329     static NETSTATISTICSGET pNetStatisticsGet = NULL;
330     static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
331     static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
332     static int is_workstation = 1;
333
334     static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
335     PERF_DATA_BLOCK *pPerfData;
336     HANDLE hDevice, hNetAPI32 = NULL;
337     DWORD dwSize, status;
338     int nDrive;
339
340     if ( !is_initialized ) {
341         HKEY hKey;
342
343         if ( debug_me )
344             log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" );
345         /* Find out whether this is an NT server or workstation if necessary */
346         if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
347                           "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
348                           0, KEY_READ, &hKey) == ERROR_SUCCESS) {
349             BYTE szValue[32];
350             dwSize = sizeof (szValue);
351
352             if ( debug_me )
353                 log_debug ("rndw32#slow_gatherer_nt: check product options\n" );
354             status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
355                                       szValue, &dwSize);
356             if (status == ERROR_SUCCESS
357                 && ascii_strcasecmp (szValue, "WinNT")) {
358                 /* Note: There are (at least) three cases for ProductType:
359                  * WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
360                  * NT Server acting as a Domain Controller */
361                 is_workstation = 0;
362                 if ( debug_me )
363                     log_debug ("rndw32: this is a NT server\n");
364             }
365             RegCloseKey (hKey);
366         }
367
368         /* Initialize the NetAPI32 function pointers if necessary */
369         if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) {
370             if ( debug_me )
371                 log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" );
372             pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
373                                                        "NetStatisticsGet");
374             pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
375                                                        "NetApiBufferSize");
376             pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
377                                                        "NetApiBufferFree");
378
379             if ( !pNetStatisticsGet
380                  || !pNetApiBufferSize || !pNetApiBufferFree ) {
381                 FreeLibrary (hNetAPI32);
382                 hNetAPI32 = NULL;
383                 g10_log_debug ("rndw32: No NETAPI found\n" );
384             }
385         }
386
387         is_initialized = 1;
388     }
389
390     /* Get network statistics.  Note: Both NT Workstation and NT Server by
391      * default will be running both the workstation and server services.  The
392      * heuristic below is probably useful though on the assumption that the
393      * majority of the network traffic will be via the appropriate service.
394      * In any case the network statistics return almost no randomness */
395     {   LPBYTE lpBuffer;
396         if (hNetAPI32 && !pNetStatisticsGet (NULL,
397                            is_workstation ? L"LanmanWorkstation" :
398                            L"LanmanServer", 0, 0, &lpBuffer) ) {
399             if ( debug_me )
400                 log_debug ("rndw32#slow_gatherer_nt: get netstats\n" );
401             pNetApiBufferSize (lpBuffer, &dwSize);
402             (*add) ( lpBuffer, dwSize,requester );
403             pNetApiBufferFree (lpBuffer);
404         }
405     }
406
407     /* Get disk I/O statistics for all the hard drives */
408     for (nDrive = 0;; nDrive++) {
409         char diskPerformance[SIZEOF_DISK_PERFORMANCE_STRUCT];
410         char szDevice[50];
411
412         /* Check whether we can access this device */
413         sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive);
414         hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
415                               NULL, OPEN_EXISTING, 0, NULL);
416         if (hDevice == INVALID_HANDLE_VALUE)
417             break;
418
419         /* Note: This only works if you have turned on the disk performance
420          * counters with 'diskperf -y'.  These counters are off by default */
421         if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
422                              diskPerformance, SIZEOF_DISK_PERFORMANCE_STRUCT,
423                              &dwSize, NULL))
424         {
425             if ( debug_me )
426                 log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n",
427                                                                   nDrive );
428             (*add) (diskPerformance, dwSize, requester );
429         }
430         else {
431             log_info ("NOTE: you should run 'diskperf -y' "
432                       "to enable the disk statistics\n");
433         }
434         CloseHandle (hDevice);
435     }
436
437   #if 0 /* we don't need this in GnuPG  */
438     /* Wait for any async keyset driver binding to complete.  You may be
439      * wondering what this call is doing here... the reason it's necessary is
440      * because RegQueryValueEx() will hang indefinitely if the async driver
441      * bind is in progress.  The problem occurs in the dynamic loading and
442      * linking of driver DLL's, which work as follows:
443      *
444      * hDriver = LoadLibrary( DRIVERNAME );
445      * pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 );
446      * pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 );
447      *
448      * If RegQueryValueEx() is called while the GetProcAddress()'s are in
449      * progress, it will hang indefinitely.  This is probably due to some
450      * synchronisation problem in the NT kernel where the GetProcAddress()
451      * calls affect something like a module reference count or function
452      * reference count while RegQueryValueEx() is trying to take a snapshot
453      * of the statistics, which include the reference counts.  Because of
454      * this, we have to wait until any async driver bind has completed
455      * before we can call RegQueryValueEx() */
456     waitSemaphore (SEMAPHORE_DRIVERBIND);
457   #endif
458
459     /* Get information from the system performance counters.  This can take
460      * a few seconds to do.  In some environments the call to
461      * RegQueryValueEx() can produce an access violation at some random time
462      * in the future, adding a short delay after the following code block
463      * makes the problem go away.  This problem is extremely difficult to
464      * reproduce, I haven't been able to get it to occur despite running it
465      * on a number of machines.  The best explanation for the problem is that
466      * on the machine where it did occur, it was caused by an external driver
467      * or other program which adds its own values under the
468      * HKEY_PERFORMANCE_DATA key.  The NT kernel calls the required external
469      * modules to map in the data, if there's a synchronisation problem the
470      * external module would write its data at an inappropriate moment,
471      * causing the access violation.  A low-level memory checker indicated
472      * that ExpandEnvironmentStrings() in KERNEL32.DLL, called an
473      * interminable number of calls down inside RegQueryValueEx(), was
474      * overwriting memory (it wrote twice the allocated size of a buffer to a
475      * buffer allocated by the NT kernel).  This may be what's causing the
476      * problem, but since it's in the kernel there isn't much which can be
477      * done.
478      *
479      * In addition to these problems the code in RegQueryValueEx() which
480      * estimates the amount of memory required to return the performance
481      * counter information isn't very accurate, since it always returns a
482      * worst-case estimate which is usually nowhere near the actual amount
483      * required.  For example it may report that 128K of memory is required,
484      * but only return 64K of data */
485     {   pPerfData =  m_alloc (cbPerfData);
486         for (;;) {
487             dwSize = cbPerfData;
488             if ( debug_me )
489                 log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
490             status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
491                                       NULL, (LPBYTE) pPerfData, &dwSize);
492             if (status == ERROR_SUCCESS) {
493                 if (!memcmp (pPerfData->Signature, L"PERF", 8)) {
494                     (*add) ( pPerfData, dwSize, requester );
495                 }
496                 else
497                     g10_log_debug ( "rndw32: no PERF signature\n");
498                 break;
499             }
500             else if (status == ERROR_MORE_DATA) {
501                 cbPerfData += PERFORMANCE_BUFFER_STEP;
502                 pPerfData = m_realloc (pPerfData, cbPerfData);
503             }
504             else {
505                 g10_log_debug ( "rndw32: get performance data problem\n");
506                 break;
507             }
508         }
509         m_free (pPerfData);
510     }
511     /* Although this isn't documented in the Win32 API docs, it's necessary
512        to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
513        implicitly opened on the first call to RegQueryValueEx()).  If this
514        isn't done then any system components which provide performance data
515        can't be removed or changed while the handle remains active */
516     RegCloseKey (HKEY_PERFORMANCE_DATA);
517 }
518
519
520 int
521 rndw32_gather_random (void (*add)(const void*, size_t, int), int requester,
522                       size_t length, int level )
523 {
524     static int is_initialized;
525     static int is_windowsNT, has_toolhelp;
526
527
528     if( !level )
529         return 0;
530     /* We don't differentiate between level 1 and 2 here because
531      * there is no nternal entropy pool as a scary resource.  It may
532      * all work slower, but because our entropy source will never
533      * block but deliver some not easy to measure entropy, we assume level 2
534      */
535
536
537     if ( !is_initialized ) {
538         OSVERSIONINFO osvi = { sizeof( osvi ) };
539         DWORD platform;
540
541         GetVersionEx( &osvi );
542         platform = osvi.dwPlatformId;
543         is_windowsNT = platform == VER_PLATFORM_WIN32_NT;
544         has_toolhelp = (platform == VER_PLATFORM_WIN32_WINDOWS
545                         || (is_windowsNT && osvi.dwMajorVersion >= 5));
546
547         if ( platform == VER_PLATFORM_WIN32s ) {
548             g10_log_fatal("can't run on a W32s platform\n" );
549         }
550         is_initialized = 1;
551         if ( debug_me )
552             log_debug ("rndw32#gather_random: platform=%d\n", (int)platform );
553     }
554
555
556     if ( debug_me )
557         log_debug ("rndw32#gather_random: req=%d len=%u lvl=%d\n",
558                            requester, (unsigned int)length, level );
559
560     if ( has_toolhelp ) {
561         slow_gatherer_windows95 ( add, requester );
562     }
563     if ( is_windowsNT ) {
564         slow_gatherer_windowsNT ( add, requester );
565     }
566
567     return 0;
568 }
569
570
571
572 int
573 rndw32_gather_random_fast( void (*add)(const void*, size_t, int), int requester )
574 {
575     static int addedFixedItems = 0;
576
577     if ( debug_me )
578         log_debug ("rndw32#gather_random_fast: req=%d\n", requester );
579
580     /* Get various basic pieces of system information: Handle of active
581      * window, handle of window with mouse capture, handle of clipboard owner
582      * handle of start of clpboard viewer list, pseudohandle of current
583      * process, current process ID, pseudohandle of current thread, current
584      * thread ID, handle of desktop window, handle  of window with keyboard
585      * focus, whether system queue has any events, cursor position for last
586      * message, 1 ms time for last message, handle of window with clipboard
587      * open, handle of process heap, handle of procs window station, types of
588      * events in input queue, and milliseconds since Windows was started */
589     {   byte buffer[20*sizeof(ulong)], *bufptr;
590         bufptr = buffer;
591       #define ADD(f)  do { ulong along = (ulong)(f);                  \
592                            memcpy (bufptr, &along, sizeof (along) );  \
593                            bufptr += sizeof (along); } while (0)
594         ADD ( GetActiveWindow ());
595         ADD ( GetCapture ());
596         ADD ( GetClipboardOwner ());
597         ADD ( GetClipboardViewer ());
598         ADD ( GetCurrentProcess ());
599         ADD ( GetCurrentProcessId ());
600         ADD ( GetCurrentThread ());
601         ADD ( GetCurrentThreadId ());
602         ADD ( GetDesktopWindow ());
603         ADD ( GetFocus ());
604         ADD ( GetInputState ());
605         ADD ( GetMessagePos ());
606         ADD ( GetMessageTime ());
607         ADD ( GetOpenClipboardWindow ());
608         ADD ( GetProcessHeap ());
609         ADD ( GetProcessWindowStation ());
610         ADD ( GetQueueStatus (QS_ALLEVENTS));
611         ADD ( GetTickCount ());
612
613         assert ( bufptr-buffer < sizeof (buffer) );
614         (*add) ( buffer, bufptr-buffer, requester );
615       #undef ADD
616     }
617
618     /* Get multiword system information: Current caret position, current
619      * mouse cursor position */
620     {   POINT point;
621         GetCaretPos (&point);
622         (*add) ( &point, sizeof (point), requester );
623         GetCursorPos (&point);
624         (*add) ( &point, sizeof (point), requester );
625     }
626
627     /* Get percent of memory in use, bytes of physical memory, bytes of free
628      * physical memory, bytes in paging file, free bytes in paging file, user
629      * bytes of address space, and free user bytes */
630     {   MEMORYSTATUS memoryStatus;
631         memoryStatus.dwLength = sizeof (MEMORYSTATUS);
632         GlobalMemoryStatus (&memoryStatus);
633         (*add) ( &memoryStatus, sizeof (memoryStatus), requester );
634     }
635
636     /* Get thread and process creation time, exit time, time in kernel mode,
637        and time in user mode in 100ns intervals */
638     {   HANDLE handle;
639         FILETIME creationTime, exitTime, kernelTime, userTime;
640         DWORD minimumWorkingSetSize, maximumWorkingSetSize;
641
642         handle = GetCurrentThread ();
643         GetThreadTimes (handle, &creationTime, &exitTime,
644                                                &kernelTime, &userTime);
645         (*add) ( &creationTime, sizeof (creationTime), requester );
646         (*add) ( &exitTime, sizeof (exitTime), requester );
647         (*add) ( &kernelTime, sizeof (kernelTime), requester );
648         (*add) ( &userTime, sizeof (userTime), requester );
649
650         handle = GetCurrentProcess ();
651         GetProcessTimes (handle, &creationTime, &exitTime,
652                                                 &kernelTime, &userTime);
653         (*add) ( &creationTime, sizeof (creationTime), requester );
654         (*add) ( &exitTime, sizeof (exitTime), requester );
655         (*add) ( &kernelTime, sizeof (kernelTime), requester );
656         (*add) ( &userTime, sizeof (userTime), requester );
657
658         /* Get the minimum and maximum working set size for the current process */
659         GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
660                                           &maximumWorkingSetSize);
661         (*add) ( &minimumWorkingSetSize,
662                                    sizeof (&minimumWorkingSetSize), requester );
663         (*add) ( &maximumWorkingSetSize,
664                                    sizeof (&maximumWorkingSetSize), requester );
665     }
666
667
668     /* The following are fixed for the lifetime of the process so we only
669      * add them once */
670     if (!addedFixedItems) {
671         STARTUPINFO startupInfo;
672
673         /* Get name of desktop, console window title, new window position and
674          * size, window flags, and handles for stdin, stdout, and stderr */
675         startupInfo.cb = sizeof (STARTUPINFO);
676         GetStartupInfo (&startupInfo);
677         (*add) ( &startupInfo, sizeof (STARTUPINFO), requester );
678         addedFixedItems = 1;
679     }
680
681     /* The performance of QPC varies depending on the architecture it's
682      * running on and on the OS.  Under NT it reads the CPU's 64-bit timestamp
683      * counter (at least on a Pentium and newer '486's, it hasn't been tested
684      * on anything without a TSC), under Win95 it reads the 1.193180 MHz PIC
685      * timer.  There are vague mumblings in the docs that it may fail if the
686      * appropriate hardware isn't available (possibly '386's or MIPS machines
687      * running NT), but who's going to run NT on a '386? */
688     {   LARGE_INTEGER performanceCount;
689         if (QueryPerformanceCounter (&performanceCount)) {
690             if ( debug_me )
691                 log_debug ("rndw32#gather_random_fast: perf data\n");
692             (*add) (&performanceCount, sizeof (&performanceCount), requester);
693         }
694         else { /* Millisecond accuracy at best... */
695             DWORD aword = GetTickCount ();
696             (*add) (&aword, sizeof (aword), requester );
697         }
698     }
699
700     return 0;
701 }
702
703
704 #endif /*USE_RNDW32*/