Merge pull request #2816 from xmcclure/profile-clean-0
[mono.git] / mono / utils / mono-rand.c
index f05e70481058e2e4e182569a75125a8312a2d52b..1e8297750bfb64858fe3f25ec5b5e30bc2b96ede 100644 (file)
@@ -10,6 +10,7 @@
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  * Copyright 2001 Xamarin, Inc (http://www.novell.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 
@@ -17,6 +18,8 @@
 #include <config.h>
 
 #include "atomic.h"
+#include "mono-error.h"
+#include "mono-error-internals.h"
 #include "mono-rand.h"
 #include "mono-threads.h"
 #include "metadata/exception.h"
@@ -97,16 +100,19 @@ mono_rand_init (guchar *seed, gint seed_size)
  * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
  * @buffer: A buffer into which to write random data.
  * @buffer_size: Number of bytes to write into buffer.
+ * @error: Set on error.
  *
- * Returns: FALSE on failure, TRUE on success.
+ * Returns: FALSE on failure and sets @error, TRUE on success.
  *
  * Extracts bytes from an RNG handle.
  */
 gboolean
-mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
+mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
 {
        HCRYPTPROV provider;
 
+       mono_error_init (error);
+
        g_assert (handle);
        provider = (HCRYPTPROV) *handle;
 
@@ -118,6 +124,7 @@ mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
                        /* exception will be thrown in managed code */
                        CryptReleaseContext (provider, 0);
                        *handle = 0;
+                       mono_error_set_execution_engine (error, "Failed to gen random bytes (%d)", GetLastError ());
                        return FALSE;
                }
        }
@@ -155,27 +162,33 @@ static gboolean use_egd = FALSE;
 static gint file = -1;
 
 static void
-get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size)
+get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size, MonoError *error)
 {
        struct sockaddr_un egd_addr;
        gint file;
        gint ret;
        guint offset = 0;
+       int err = 0;
 
+       mono_error_init (error);
+       
        file = socket (PF_UNIX, SOCK_STREAM, 0);
        if (file < 0) {
                ret = -1;
+               err = errno;
        } else {
                egd_addr.sun_family = AF_UNIX;
                strncpy (egd_addr.sun_path, path, sizeof (egd_addr.sun_path) - 1);
                egd_addr.sun_path [sizeof (egd_addr.sun_path) - 1] = '\0';
                ret = connect (file, (struct sockaddr*) &egd_addr, sizeof (egd_addr));
+               err = errno;
        }
        if (ret == -1) {
                if (file >= 0)
                        close (file);
                g_warning ("Entropy problem! Can't create or connect to egd socket %s", path);
-               mono_raise_exception (mono_get_exception_execution_engine ("Failed to open egd socket"));
+               mono_error_set_execution_engine (error, "Failed to open egd socket %s: %s", path, strerror (err));
+               return;
        }
 
        while (buffer_size > 0) {
@@ -187,14 +200,16 @@ get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size)
                request [1] = buffer_size < 255 ? buffer_size : 255;
                while (count < 2) {
                        int sent = write (file, request + count, 2 - count);
+                       err = errno;
                        if (sent >= 0) {
                                count += sent;
-                       } else if (errno == EINTR) {
+                       } else if (err == EINTR) {
                                continue;
                        } else {
                                close (file);
-                               g_warning ("Send egd request failed %d", errno);
-                               mono_raise_exception (mono_get_exception_execution_engine ("Failed to send request to egd socket"));
+                               g_warning ("Send egd request failed %d", err);
+                               mono_error_set_execution_engine (error, "Failed to send request to egd socket: %s", strerror (err));
+                               return;
                        }
                }
 
@@ -202,15 +217,17 @@ get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size)
                while (count != request [1]) {
                        int received;
                        received = read (file, buffer + offset, request [1] - count);
+                       err = errno;
                        if (received > 0) {
                                count += received;
                                offset += received;
-                       } else if (received < 0 && errno == EINTR) {
+                       } else if (received < 0 && err == EINTR) {
                                continue;
                        } else {
                                close (file);
-                               g_warning ("Receive egd request failed %d", errno);
-                               mono_raise_exception (mono_get_exception_execution_engine ("Failed to get response from egd socket"));
+                               g_warning ("Receive egd request failed %d", err);
+                               mono_error_set_execution_engine (error, "Failed to get response from egd socket: %s", strerror(err));
+                               return;
                        }
                }
 
@@ -253,10 +270,12 @@ mono_rand_init (guchar *seed, gint seed_size)
 }
 
 gboolean
-mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
+mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
 {
        g_assert (handle);
 
+       mono_error_init (error);
+
        if (use_egd) {
                const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
                /* exception will be thrown in managed code */
@@ -264,7 +283,7 @@ mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
                        *handle = NULL;
                        return FALSE;
                }
-               get_entropy_from_egd (socket_path, buffer, buffer_size);
+               get_entropy_from_egd (socket_path, buffer, buffer_size, error);
        } else {
                /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
                gint count = 0;
@@ -277,6 +296,7 @@ mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
                                        continue;
                                g_warning("Entropy error! Error in read (%s).", strerror (errno));
                                /* exception will be thrown in managed code */
+                               mono_error_set_execution_engine (error, "Entropy error! Error in read (%s).", strerror (errno));
                                return FALSE;
                        }
                        count += err;
@@ -319,10 +339,12 @@ mono_rand_init (guchar *seed, gint seed_size)
 }
 
 gboolean
-mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size)
+mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
 {
        gint count = 0;
 
+       mono_error_init (error);
+       
        do {
                if (buffer_size - count >= sizeof (gint32) && RAND_MAX >= 0xFFFFFFFF) {
                        *(gint32*) buffer = rand();
@@ -361,10 +383,10 @@ mono_rand_close (gpointer provider)
  * Extracts one 32-bit unsigned int from an RNG handle.
  */
 gboolean
-mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max)
+mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error)
 {
        g_assert (val);
-       if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32)))
+       if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error))
                return FALSE;
 
        double randomDouble = ((gdouble) *val) / ( ((double)G_MAXUINT32) + 1 ); // Range is [0,1)
@@ -374,4 +396,4 @@ mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 m
        g_assert (*val <= max);
 
        return TRUE;
-}
\ No newline at end of file
+}