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
;
54 mono_error_init (error
);
56 socket_fd
= socket (PF_UNIX
, SOCK_STREAM
, 0);
61 egd_addr
.sun_family
= AF_UNIX
;
62 strncpy (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_getenv("MONO_EGD_SOCKET") != NULL
;
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
)
158 mono_error_init (error
);
161 const 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
);
169 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
174 err
= read (file
, buffer
+ count
, buffer_size
- count
);
178 g_warning("Entropy error! Error in read (%s).", strerror (errno
));
179 /* exception will be thrown in managed code */
180 mono_error_set_execution_engine (error
, "Entropy error! Error in read (%s).", strerror (errno
));
184 } while (count
< buffer_size
);
190 mono_rand_close (gpointer provider
)
200 mono_rand_open (void)
202 static gint32 status
= 0;
203 if (status
!= 0 || InterlockedCompareExchange (&status
, 1, 0) != 0) {
205 mono_thread_info_yield ();
217 mono_rand_init (guchar
*seed
, gint seed_size
)
219 return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer
223 mono_rand_try_get_bytes (gpointer
*handle
, guchar
*buffer
, gint buffer_size
, MonoError
*error
)
227 mono_error_init (error
);
230 if (buffer_size
- count
>= sizeof (gint32
) && RAND_MAX
>= 0xFFFFFFFF) {
231 *(gint32
*) buffer
= rand();
232 count
+= sizeof (gint32
);
233 buffer
+= sizeof (gint32
) / sizeof (guchar
);
234 } else if (buffer_size
- count
>= sizeof (gint16
) && RAND_MAX
>= 0xFFFF) {
235 *(gint16
*) buffer
= rand();
236 count
+= sizeof (gint16
);
237 buffer
+= sizeof (gint16
) / sizeof (guchar
);
238 } else if (buffer_size
- count
>= sizeof (gint8
) && RAND_MAX
>= 0xFF) {
239 *(gint8
*) buffer
= rand();
240 count
+= sizeof (gint8
);
241 buffer
+= sizeof (gint8
) / sizeof (guchar
);
243 } while (count
< buffer_size
);
249 mono_rand_close (gpointer provider
)
256 * mono_rand_try_get_uint32:
257 * @handle: A pointer to an RNG handle. Handle is set to NULL on failure.
258 * @val: A pointer to a 32-bit unsigned int, to which the result will be written.
259 * @min: Result will be greater than or equal to this value.
260 * @max: Result will be less than or equal to this value.
262 * Returns: FALSE on failure, TRUE on success.
264 * Extracts one 32-bit unsigned int from an RNG handle.
267 mono_rand_try_get_uint32 (gpointer
*handle
, guint32
*val
, guint32 min
, guint32 max
, MonoError
*error
)
270 if (!mono_rand_try_get_bytes (handle
, (guchar
*) val
, sizeof (guint32
), error
))
273 double randomDouble
= ((gdouble
) *val
) / ( ((double)G_MAXUINT32
) + 1 ); // Range is [0,1)
274 *val
= (guint32
) (randomDouble
* (max
- min
+ 1) + min
);
276 g_assert (*val
>= min
);
277 g_assert (*val
<= max
);