Freeciv-3.3: /fast/freeciv/nightly/main/utility/randseed.c Source File

Freeciv-3.3

Loading...
Searching...
No Matches
randseed.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 2021 The Freeciv Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14/* Random number initialization, possibly system-dependent */
15
16#ifdef HAVE_CONFIG_H
17#include <fc_config.h>
18#endif
19
20#include "fc_prehdrs.h"
21
22#ifdef HAVE_BCRYPT_H
23#include <bcrypt.h>
24#endif
25#include <errno.h>
26#include <fcntl.h>
27#include <stdint.h>
28#include <string.h>
29#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETENTROPY)
30#include <sys/random.h>
31#endif
32#include <sys/stat.h>
33#include <time.h>
34#ifdef HAVE_UNISTD_H
35#include <unistd.h>
36#endif
37#ifdef FREECIV_MSWINDOWS
38#include <ntdef.h>
39#endif
40
41/* utility */
42#include "fcintl.h"
43#include "log.h"
44
45#include "randseed.h"
46
47/**********************************************************************/
52 static bool generate_seed_getentropy(randseed *ret)
53{
54#if HAVE_GETENTROPY
55 /* getentropy() is from OpenBSD, and should be supported on at least
56 * FreeBSD and glibc on Linux (as a wrapper to getrandom()) as well.
57 */
58 randseed seed = 0;
59
60 if (getentropy(&seed, sizeof(seed)) == 0) {
61 *ret = seed;
62
63 return TRUE;
64 } else {
65 log_error(_("getentropy() failed: %s"), strerror(errno));
66 }
67#endif /* HAVE_GETENTROPY */
68
69 return FALSE;
70}
71
72/**********************************************************************/
77 static bool generate_seed_bcryptgenrandom(randseed *ret)
78{
79#ifdef HAVE_BCRYPTGENRANDOM
80 NTSTATUS Status;
81
82 Status = BCryptGenRandom(NULL, (PUCHAR)ret, sizeof(randseed),
83 BCRYPT_USE_SYSTEM_PREFERRED_RNG);
84
85 if (NT_SUCCESS(Status)) {
86 return TRUE;
87 }
88#endif /* HAVE_BCRYPTGENRANDOM */
89
90 return FALSE;
91}
92
93/**********************************************************************/
98 static bool generate_seed_urandom(randseed *ret)
99{
100#if HAVE_USABLE_URANDOM
101 /*
102 * /dev/urandom should be available on most Unixen. The Wikipedia page
103 * mentions Linux, FreeBSD, OpenBSD, macOS as well as Solaris, NetBSD,
104 * Tru64 UNIX 5.1B, AIX 5.2 and HP-UX 11i v2.
105 *
106 * However, opening it may fail if the game is running in a chroot without
107 * it, or is otherwise restricted from accessing it. This is why getentropy()
108 * is preferred.
109 */
110 static const char *random_device = "/dev/urandom";
111 int fd = 0;
112 randseed seed = 0;
113 bool ok = FALSE;
114
115 /* stdio would read an unnecessarily large block, which may mess up users
116 * /dev/random on Linux, so use the low-level calls instead. */
117 fd = open(random_device, O_RDONLY);
118 if (fd == -1) {
119 /* Only warning as we fallback to other randseed generation methods */
120 log_warn(_("Opening %s failed: %s"), random_device, strerror(errno));
121 } else {
122 int n = read(fd, &seed, sizeof(seed));
123
124 if (n == -1) {
125 log_warn(_("Reading %s failed: %s"), random_device, strerror(errno));
126 } else if (n != sizeof(seed)) {
127 log_warn(_("Reading %s: short read without error"), random_device);
128 } else {
129 ok = 1;
130 }
131 close(fd);
132 }
133 if (ok) {
134 *ret = seed;
135
136 return TRUE;
137 }
138#endif /* HAVE_USABLE_URANDOM */
139
140 return FALSE;
141}
142
143/**********************************************************************/
148 static bool generate_seed_clock_gettime(randseed *ret)
149{
150#if HAVE_CLOCK_GETTIME
151 /*
152 * clock_gettime() nominally gives nanoseconds, but the real granularity may be
153 * worse, making the lowest bits less useful. On the other hand, the lower bits
154 * of full seconds are the most useful, the high bits being very predictable.
155 * Xor them together to hopefully get something relatively unpredictable in the
156 * bottom 30 bits.
157 */
158 randseed seed = 0;
159 struct timespec tp;
160
161 if (clock_gettime(CLOCK_REALTIME, &tp) == 0) {
162 seed = tp.tv_nsec;
163 seed ^= tp.tv_sec;
164 *ret = seed;
165
166 return TRUE;
167 } else {
168 /* This shouldn't fail if the function is supported on the system */
169 log_error(_("clock_gettime(CLOCK_REALTIME) failed: %s"), strerror(errno));
170 }
171#endif /* HAVE_CLOCK_GETTIME */
172
173 return FALSE;
174}
175
176/**********************************************************************/
180 static bool generate_seed_time(randseed *ret)
181{
182 /* No reasonable way this can fail */
183 *ret = (randseed) time(NULL);
184
185 return TRUE;
186}
187
188/**********************************************************************/
192 randseed generate_game_seed(void)
193{
194 randseed seed = 0;
195
196 /* Good random sources */
197 if (generate_seed_getentropy(&seed)) {
198 log_debug("Got random seed from getentropy()");
199 return seed;
200 }
201 if (generate_seed_bcryptgenrandom(&seed)) {
202 log_debug("Got random seed from BCryptGenRandom()");
203 return seed;
204 }
205 if (generate_seed_urandom(&seed)) {
206 log_debug("Got random seed from urandom()");
207 return seed;
208 }
209
210 /* Not so good random source */
211 log_normal(_("No good random source usable. Falling back to time-based random seeding."));
212
213 if (generate_seed_clock_gettime(&seed)) {
214 log_debug("Got random seed with clock_gettime()");
215
216 return seed;
217 }
218
219 /* Lousy last-case source */
220 log_warn(_("Falling back to predictable random seed from current coarse-granularity time."));
221 generate_seed_time(&seed);
222 log_debug("Got random seed from time()");
223
224 return seed;
225}
n
#define n
Definition astring.c:77
incite_cost
char * incite_cost
Definition comments.c:74
fc_prehdrs.h
fcintl.h
_
#define _(String)
Definition fcintl.h:67
log.h
log_warn
#define log_warn(message,...)
Definition log.h:105
log_debug
#define log_debug(message,...)
Definition log.h:115
log_normal
#define log_normal(message,...)
Definition log.h:107
log_error
#define log_error(message,...)
Definition log.h:103
generate_seed_getentropy
static bool generate_seed_getentropy(randseed *ret)
Definition randseed.c:52
generate_seed_clock_gettime
static bool generate_seed_clock_gettime(randseed *ret)
Definition randseed.c:148
generate_seed_bcryptgenrandom
static bool generate_seed_bcryptgenrandom(randseed *ret)
Definition randseed.c:77
generate_game_seed
randseed generate_game_seed(void)
Definition randseed.c:192
generate_seed_urandom
static bool generate_seed_urandom(randseed *ret)
Definition randseed.c:98
generate_seed_time
static bool generate_seed_time(randseed *ret)
Definition randseed.c:180
randseed.h
randseed
unsigned int randseed
Definition randseed.h:23
TRUE
#define TRUE
Definition support.h:46
FALSE
#define FALSE
Definition support.h:47