[System] Use GZipStream from corefx
[mono-project.git] / mono / utils / mono-rand.c
blob1ac91179382d2ffb4ee7f5e826bca141e7cc4bb5
1 /*
2 * mono-rand.c:
4 * Authors:
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.
16 #include <glib.h>
17 #include <config.h>
19 #include "atomic.h"
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"
27 #ifdef HOST_WIN32
28 // Windows specific implementation in mono-rand-windows.c
29 #elif defined (HAVE_SYS_UN_H) && !defined(__native_client__)
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <sys/socket.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
38 #ifndef NAME_DEV_URANDOM
39 #define NAME_DEV_URANDOM "/dev/urandom"
40 #endif
42 static gboolean use_egd = FALSE;
43 static gint file = -1;
45 static void
46 get_entropy_from_egd (const char *path, guchar *buffer, int buffer_size, MonoError *error)
48 struct sockaddr_un egd_addr;
49 gint socket_fd;
50 gint ret;
51 guint offset = 0;
52 int err = 0;
54 mono_error_init (error);
56 socket_fd = socket (PF_UNIX, SOCK_STREAM, 0);
57 if (socket_fd < 0) {
58 ret = -1;
59 err = errno;
60 } else {
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));
65 err = errno;
67 if (ret == -1) {
68 if (socket_fd >= 0)
69 close (socket_fd);
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));
72 return;
75 while (buffer_size > 0) {
76 guchar request [2];
77 gint count = 0;
79 /* block until daemon can return enough entropy */
80 request [0] = 2;
81 request [1] = buffer_size < 255 ? buffer_size : 255;
82 while (count < 2) {
83 int sent = write (socket_fd, request + count, 2 - count);
84 err = errno;
85 if (sent >= 0) {
86 count += sent;
87 } else if (err == EINTR) {
88 continue;
89 } else {
90 close (socket_fd);
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));
93 return;
97 count = 0;
98 while (count != request [1]) {
99 int received;
100 received = read (socket_fd, buffer + offset, request [1] - count);
101 err = errno;
102 if (received > 0) {
103 count += received;
104 offset += received;
105 } else if (received < 0 && err == EINTR) {
106 continue;
107 } else {
108 close (socket_fd);
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));
111 return;
115 buffer_size -= request [1];
118 close (socket_fd);
121 gboolean
122 mono_rand_open (void)
124 static gint32 status = 0;
125 if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
126 while (status != 2)
127 mono_thread_info_yield ();
128 return TRUE;
131 #ifdef NAME_DEV_URANDOM
132 file = open (NAME_DEV_URANDOM, O_RDONLY);
133 #endif
134 #ifdef NAME_DEV_RANDOM
135 if (file < 0)
136 file = open (NAME_DEV_RANDOM, O_RDONLY);
137 #endif
138 if (file < 0)
139 use_egd = g_getenv("MONO_EGD_SOCKET") != NULL;
141 status = 2;
143 return TRUE;
146 gpointer
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);
153 gboolean
154 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
156 g_assert (handle);
158 mono_error_init (error);
160 if (use_egd) {
161 const char *socket_path = g_getenv ("MONO_EGD_SOCKET");
162 /* exception will be thrown in managed code */
163 if (socket_path == NULL) {
164 *handle = NULL;
165 return FALSE;
167 get_entropy_from_egd (socket_path, buffer, buffer_size, error);
168 } else {
169 /* Read until the buffer is filled. This may block if using NAME_DEV_RANDOM. */
170 gint count = 0;
171 gint err;
173 do {
174 err = read (file, buffer + count, buffer_size - count);
175 if (err < 0) {
176 if (errno == EINTR)
177 continue;
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));
181 return FALSE;
183 count += err;
184 } while (count < buffer_size);
186 return TRUE;
189 void
190 mono_rand_close (gpointer provider)
194 #else
196 #include <stdlib.h>
197 #include <time.h>
199 gboolean
200 mono_rand_open (void)
202 static gint32 status = 0;
203 if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) {
204 while (status != 2)
205 mono_thread_info_yield ();
206 return TRUE;
209 srand (time (NULL));
211 status = 2;
213 return TRUE;
216 gpointer
217 mono_rand_init (guchar *seed, gint seed_size)
219 return "srand"; // NULL will be interpreted as failure; return arbitrary nonzero pointer
222 gboolean
223 mono_rand_try_get_bytes (gpointer *handle, guchar *buffer, gint buffer_size, MonoError *error)
225 gint count = 0;
227 mono_error_init (error);
229 do {
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);
245 return TRUE;
248 void
249 mono_rand_close (gpointer provider)
253 #endif
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.
266 gboolean
267 mono_rand_try_get_uint32 (gpointer *handle, guint32 *val, guint32 min, guint32 max, MonoError *error)
269 g_assert (val);
270 if (!mono_rand_try_get_bytes (handle, (guchar*) val, sizeof (guint32), error))
271 return FALSE;
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);
279 return TRUE;