/* getrandom.c - Libgcrypt Random Number client
* Copyright (C) 2006 Free Software Foundation, Inc.
*
* Getrandom is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Getrandom is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <config.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#define PGM "getrandom"
#define MYVERSION_LINE PGM " (Libgcrypt) " VERSION
#define BUGREPORT_LINE "\nReport bugs to <bug-libgcrypt@gnupg.org>.\n"
static void
logit (const char *format, ...)
{
va_list arg_ptr;
va_start (arg_ptr, format) ;
fputs (PGM ": ", stderr);
vfprintf (stderr, format, arg_ptr);
putc ('\n', stderr);
va_end (arg_ptr);
}
/* Send LENGTH bytes of BUFFER to file descriptor FD. Returns 0 on
success or another value on write error. */
static int
writen (int fd, const void *buffer, size_t length)
{
while (length)
{
ssize_t n;
do
n = write (fd, buffer, length);
while (n < 0 && errno == EINTR);
if (n < 0)
{
logit ("write error: %s", strerror (errno));
return -1; /* write error */
}
length -= n;
buffer = (const char *)buffer + n;
}
return 0; /* Okay */
}
static void
print_version (int with_help)
{
fputs (MYVERSION_LINE "\n"
"Copyright (C) 2006 Free Software Foundation, Inc.\n"
"License GPLv2+: GNU GPL version 2 or later "
"<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n",
stdout);
if (with_help)
fputs ("\n"
"Usage: " PGM " [OPTIONS] NBYTES\n"
"Connect to libgcrypt's random number daemon and "
"return random numbers"
"\n"
" --nonce Return weak random suitable for a nonce\n"
" --very-strong Return very strong random\n"
" --ping Send a ping\n"
" --socket NAME Name of sockket to connect to\n"
" --hex Return result as a hex dump\n"
" --verbose Show what we are doing\n"
" --version Print version of the program and exit\n"
" --help Display this help and exit\n"
BUGREPORT_LINE, stdout );
exit (0);
}
static int
print_usage (void)
{
fputs ("usage: " PGM " [OPTIONS] NBYTES\n", stderr);
fputs (" (use --help to display options)\n", stderr);
exit (1);
}
int
main (int argc, char **argv)
{
struct sockaddr_un *srvr_addr;
socklen_t addrlen;
int fd;
int rc;
unsigned char buffer[300];
int nleft, nread;
const char *socketname = "/var/run/libgcrypt/S.gcryptrnd";
int do_ping = 0;
int get_nonce = 0;
int get_very_strong = 0;
int req_nbytes, nbytes, n;
int verbose = 0;
int fail = 0;
int do_hex = 0;
if (argc)
{
argc--; argv++;
}
while (argc && **argv == '-' && (*argv)[1] == '-')
{
if (!(*argv)[2])
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--version"))
print_version (0);
else if (!strcmp (*argv, "--help"))
print_version (1);
else if (!strcmp (*argv, "--socket") && argc > 1 )
{
argc--; argv++;
socketname = *argv;
argc--; argv++;
}
else if (!strcmp (*argv, "--nonce"))
{
argc--; argv++;
get_nonce = 1;
}
else if (!strcmp (*argv, "--very-strong"))
{
argc--; argv++;
get_very_strong = 1;
}
else if (!strcmp (*argv, "--ping"))
{
argc--; argv++;
do_ping = 1;
}
else if (!strcmp (*argv, "--hex"))
{
argc--; argv++;
do_hex = 1;
}
else if (!strcmp (*argv, "--verbose"))
{
argc--; argv++;
verbose = 1;
}
else
print_usage ();
}
if (!argc && do_ping)
; /* This is allowed. */
else if (argc != 1)
print_usage ();
req_nbytes = argc? atoi (*argv) : 0;
if (req_nbytes < 0)
print_usage ();
/* Create a socket. */
fd = socket (AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
{
logit ("can't create socket: %s", strerror (errno));
exit (1);
}
srvr_addr = malloc (sizeof *srvr_addr);
if (!srvr_addr)
{
logit ("malloc failed: %s", strerror (errno));
exit (1);
}
memset (srvr_addr, 0, sizeof *srvr_addr);
srvr_addr->sun_family = AF_UNIX;
if (strlen (socketname) + 1 >= sizeof (srvr_addr->sun_path))
{
logit ("socket name `%s' too long", socketname);
exit (1);
}
strcpy (srvr_addr->sun_path, socketname);
addrlen = (offsetof (struct sockaddr_un, sun_path)
+ strlen (srvr_addr->sun_path) + 1);
rc = connect (fd, (struct sockaddr*) srvr_addr, addrlen);
if (rc == -1)
{
logit ("error connecting socket `%s': %s",
srvr_addr->sun_path, strerror (errno));
close (fd);
exit (1);
}
do
{
nbytes = req_nbytes > 255? 255 : req_nbytes;
req_nbytes -= nbytes;
buffer[0] = 3;
if (do_ping)
buffer[1] = 0;
else if (get_nonce)
buffer[1] = 10;
else if (get_very_strong)
buffer[1] = 12;
else
buffer[1] = 11;
buffer[2] = nbytes;
if (writen (fd, buffer, 3))
fail = 1;
else
{
for (nleft=2, nread=0; nleft > 0; )
{
do
n = read (fd, buffer+nread, nleft);
while (n < 0 && errno == EINTR);
if (n < 0)
{
logit ("read error: %s", strerror (errno));
exit (1);
}
nleft -= n;
nread += n;
if (nread && buffer[0])
{
logit ("server returned error code %d", buffer[0]);
exit (1);
}
}
if (verbose)
logit ("received response with %d bytes of data", buffer[1]);
if (buffer[1] < nbytes)
{
logit ("warning: server returned less bytes than requested");
fail = 1;
}
else if (buffer[1] > nbytes && !do_ping)
{
logit ("warning: server returned more bytes than requested");
fail = 1;
}
nbytes = buffer[1];
if (nbytes > sizeof buffer)
{
logit ("buffer too short to receive data");
exit (1);
}
for (nleft=nbytes, nread=0; nleft > 0; )
{
do
n = read (fd, buffer+nread, nleft);
while (n < 0 && errno == EINTR);
if (n < 0)
{
logit ("read error: %s", strerror (errno));
exit (1);
}
nleft -= n;
nread += n;
}
if (do_hex)
{
for (n=0; n < nbytes; n++)
{
if (!n)
;
else if (!(n % 16))
putchar ('\n');
else
putchar (' ');
printf ("%02X", buffer[n]);
}
if (nbytes)
putchar ('\n');
}
else
{
if (fwrite (buffer, nbytes, 1, stdout) != 1)
{
logit ("error writing to stdout: %s", strerror (errno));
fail = 1;
}
}
}
}
while (!fail && req_nbytes);
close (fd);
free (srvr_addr);
return fail? 1 : 0;
}