5 * Mark Crichton (crichton@gimp.org)
6 * Patrik Torstensson (p@rxc.se)
7 * Sebastien Pouliot (sebastien@ximian.com)
8 * Ludovic Henry (ludovic.henry@xamarin.com)
10 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12 * Copyright 2001 Xamarin, Inc (http://www.novell.com)
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
20 #include "mono-error.h"
21 #include "mono-error-internals.h"
22 #include "mono-rand.h"
23 #include "mono-threads.h"
24 #include "metadata/exception.h"
25 #include "metadata/object.h"
28 // Windows specific implementation in mono-rand-windows.c
29 #elif defined (HAVE_SYS_UN_H) && !defined(__native_client__)
34 #include <sys/socket.h>
35 #include <sys/types.h>
38 #ifndef NAME_DEV_URANDOM
39 #define NAME_DEV_URANDOM "/dev/urandom"
42 static gboolean use_egd = FALSE;
43 static gint file = -1;
46 get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size, MonoError *error)
48 struct sockaddr_un egd_addr;
56 socket_fd = socket (PF_UNIX, SOCK_STREAM, 0);
61 egd_addr.sun_family = AF_UNIX;
62 memcpy (egd_addr.sun_path, path, sizeof (egd_addr.sun_path) - 1);
63 egd_addr.sun_path [sizeof (egd_addr.sun_path) - 1] = '\0';
64 ret = connect (socket_fd, (struct sockaddr*) &egd_addr, sizeof (egd_addr));
70 g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
71 mono_error_set_execution_engine (error, "Failed to open egd socket %s: %s", path, strerror (err));
75 while (buffer_size > 0) {
79 /* block until daemon can return enough entropy */
81 request [1] = buffer_size < 255 ? buffer_size : 255;
83 int sent = write (socket_fd, request + count, 2 - count);
87 } else if (err == EINTR) {
91 g_warning ("Send egd request failed %d", err);
92 mono_error_set_execution_engine (error, "Failed to send request to egd socket: %s", strerror (err));
98 while (count != request [1]) {
100 received = read (socket_fd, buffer + offset, request [1] - count);
105 } else if (received < 0 && err == EINTR) {
109 g_warning ("Receive egd request failed %d", err);
110 mono_error_set_execution_engine (error, "Failed to get response from egd socket: %s", strerror(err));
115 buffer_size -= request [1];
122 mono_rand_open (void)
124 static gint32 status = 0;
125 if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
127 mono_thread_info_yield ();
131 #ifdef NAME_DEV_URANDOM
132 file = open (NAME_DEV_URANDOM, O_RDONLY);
134 #ifdef NAME_DEV_RANDOM
136 file = open (NAME_DEV_RANDOM, O_RDONLY);
139 use_egd = g_hasenv ("MONO_EGD_SOCKET");
147 mono_rand_init (guchar *seed, gint seed_size)
149 // file < 0 is expected in the egd case
150 return (!use_egd && file < 0) ? NULL : GINT_TO_POINTER (file);
154 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
161 char *socket_path = g_getenv ("MONO_EGD_SOCKET");
162 /* exception will be thrown in managed code */
163 if (socket_path == NULL) {
167 get_entropy_from_egd (socket_path, buffer, buffer_size, error);
168 g_free (socket_path);
170 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
175 err = read (file, buffer + count, buffer_size - count);
179 g_warning("Entropy error! Error in read (%s).", strerror (errno));
180 /* exception will be thrown in managed code */
181 mono_error_set_execution_engine (error, "Entropy error! Error in read (%s).", strerror (errno));
185 } while (count < buffer_size);
191 mono_rand_close (gpointer provider)
201 mono_rand_open (void)
203 static gint32 status = 0;
204 if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
206 mono_thread_info_yield ();
218 mono_rand_init (guchar *seed, gint seed_size)
220 return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer
224 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
231 if (buffer_size - count >= sizeof (gint32) && RAND_MAX >= 0xFFFFFFFF) {
232 *(gint32*) buffer = rand();
233 count += sizeof (gint32);
234 buffer += sizeof (gint32) / sizeof (guchar);
235 } else if (buffer_size - count >= sizeof (gint16) && RAND_MAX >= 0xFFFF) {
236 *(gint16*) buffer = rand();
237 count += sizeof (gint16);
238 buffer += sizeof (gint16) / sizeof (guchar);
239 } else if (buffer_size - count >= sizeof (gint8) && RAND_MAX >= 0xFF) {
240 *(gint8*) buffer = rand();
241 count += sizeof (gint8);
242 buffer += sizeof (gint8) / sizeof (guchar);
244 } while (count < buffer_size);
250 mono_rand_close (gpointer provider)
257 * mono_rand_try_get_uint32:
258 * \param handle A pointer to an RNG handle. Handle is set to NULL on failure.
259 * \param val A pointer to a 32-bit unsigned int, to which the result will be written.
260 * \param min Result will be greater than or equal to this value.
261 * \param max Result will be less than or equal to this value.
262 * Extracts one 32-bit unsigned int from an RNG handle.
263 * \returns FALSE on failure, TRUE on success.
266 mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error)
269 if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error))
272 double randomDouble = ((gdouble) *val) / ( ((double)G_MAXUINT32) + 1 ); // Range is [0,1)
273 *val = (guint32) (randomDouble * (max - min + 1) + min);
275 g_assert (*val >= min);
276 g_assert (*val <= max);