1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00
gnupg/tools/sockprox.c
Werner Koch 096e7457ec Change all quotes in strings and comments to the new GNU standard.
The asymmetric quotes used by GNU in the past (`...') don't render
nicely on modern systems.  We now use two \x27 characters ('...').

The proper solution would be to use the correct Unicode symmetric
quotes here.  However this has the disadvantage that the system
requires Unicode support.  We don't want that today.  If Unicode is
available a generated po file can be used to output proper quotes.  A
simple sed script like the one used for en@quote is sufficient to
change them.

The changes have been done by applying

  sed -i "s/\`\([^'\`]*\)'/'\1'/g"

to most files and fixing obvious problems by hand.  The msgid strings in
the po files were fixed with a similar command.
2012-06-05 19:29:22 +02:00

551 lines
10 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* sockprox - Proxy for local sockets with logging facilities
* Copyright (C) 2007 g10 Code GmbH.
*
* sockprox 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 3 of the License, or
* (at your option) any later version.
*
* sockprox 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, see <http://www.gnu.org/licenses/>.
*/
/* Hacked by Moritz Schulte <moritz@g10code.com>.
Usage example:
Run a server which binds to a local socket. For example,
gpg-agent. gpg-agent's local socket is specified with --server.
sockprox opens a new local socket (here "mysock"); the whole
traffic between server and client is written to "/tmp/prot" in this
case.
./sockprox --server /tmp/gpg-PKdD8r/S.gpg-agent.ssh \
--listen mysock --protocol /tmp/prot
Then, redirect your ssh-agent client to sockprox by setting
SSH_AUTH_SOCK to "mysock".
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <assert.h>
#include <pthread.h>
struct opt
{
char *protocol_file;
char *server_spec;
char *listen_spec;
int verbose;
};
struct opt opt = { NULL, NULL, NULL, 0 };
struct thread_data
{
int client_sock;
FILE *protocol_file;
};
static int
create_server_socket (const char *filename, int *new_sock)
{
struct sockaddr_un name;
size_t size;
int sock;
int ret;
int err;
/* Create the socket. */
sock = socket (PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0)
{
err = errno;
goto out;
}
/* Bind a name to the socket. */
name.sun_family = AF_LOCAL;
strncpy (name.sun_path, filename, sizeof (name.sun_path));
name.sun_path[sizeof (name.sun_path) - 1] = '\0';
size = SUN_LEN (&name);
remove (filename);
ret = bind (sock, (struct sockaddr *) &name, size);
if (ret < 0)
{
err = errno;
goto out;
}
ret = listen (sock, 2);
if (ret < 0)
{
err = errno;
goto out;
}
*new_sock = sock;
err = 0;
out:
return err;
}
static int
connect_to_socket (const char *filename, int *new_sock)
{
struct sockaddr_un srvr_addr;
size_t len;
int sock;
int ret;
int err;
sock = socket (PF_LOCAL, SOCK_STREAM, 0);
if (sock == -1)
{
err = errno;
goto out;
}
memset (&srvr_addr, 0, sizeof srvr_addr);
srvr_addr.sun_family = AF_LOCAL;
strncpy (srvr_addr.sun_path, filename, sizeof (srvr_addr.sun_path) - 1);
srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
len = SUN_LEN (&srvr_addr);
ret = connect (sock, (struct sockaddr *) &srvr_addr, len);
if (ret == -1)
{
close (sock);
err = errno;
goto out;
}
*new_sock = sock;
err = 0;
out:
return err;
}
static int
log_data (unsigned char *data, size_t length,
FILE *from, FILE *to, FILE *protocol)
{
unsigned int i;
int ret;
int err;
flockfile (protocol);
fprintf (protocol, "%i -> %i: ", fileno (from), fileno (to));
for (i = 0; i < length; i++)
fprintf (protocol, "%02X", data[i]);
fprintf (protocol, "\n");
funlockfile (protocol);
ret = fflush (protocol);
if (ret == EOF)
err = errno;
else
err = 0;
return err;
}
static int
transfer_data (FILE *from, FILE *to, FILE *protocol)
{
unsigned char buffer[BUFSIZ];
size_t len, written;
int err;
int ret;
err = 0;
while (1)
{
len = fread (buffer, 1, sizeof (buffer), from);
if (len == 0)
break;
err = log_data (buffer, len, from, to, protocol);
if (err)
break;
written = fwrite (buffer, 1, len, to);
if (written != len)
{
err = errno;
break;
}
ret = fflush (to);
if (ret == EOF)
{
err = errno;
break;
}
if (ferror (from))
break;
}
return err;
}
static int
io_loop (FILE *client, FILE *server, FILE *protocol)
{
fd_set active_fd_set, read_fd_set;
int ret;
int err;
FD_ZERO (&active_fd_set);
FD_SET (fileno (client), &active_fd_set);
FD_SET (fileno (server), &active_fd_set);
err = 0;
while (1)
{
read_fd_set = active_fd_set;
/* FIXME: eof? */
ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
if (ret < 0)
{
err = errno;
break;
}
if (FD_ISSET (fileno (client), &read_fd_set))
{
if (feof (client))
break;
/* Forward data from client to server. */
err = transfer_data (client, server, protocol);
}
else if (FD_ISSET (fileno (server), &read_fd_set))
{
if (feof (server))
break;
/* Forward data from server to client. */
err = transfer_data (server, client, protocol);
}
if (err)
break;
}
return err;
}
/* Set the 'O_NONBLOCK' flag of DESC if VALUE is nonzero,
or clear the flag if VALUE is 0.
Return 0 on success, or -1 on error with 'errno' set. */
int
set_nonblock_flag (int desc, int value)
{
int oldflags = fcntl (desc, F_GETFL, 0);
int err;
int ret;
/* If reading the flags failed, return error indication now. */
if (oldflags == -1)
return -1;
/* Set just the flag we want to set. */
if (value != 0)
oldflags |= O_NONBLOCK;
else
oldflags &= ~O_NONBLOCK;
/* Store modified flag word in the descriptor. */
ret = fcntl (desc, F_SETFL, oldflags);
if (ret == -1)
err = errno;
else
err = 0;
return err;
}
void *
serve_client (void *data)
{
struct thread_data *thread_data = data;
int client_sock = thread_data->client_sock;
int server_sock;
FILE *protocol = thread_data->protocol_file;
FILE *client;
FILE *server;
int err;
client = NULL;
server = NULL;
/* Connect to server. */
err = connect_to_socket (opt.server_spec, &server_sock);
if (err)
goto out;
/* Set IO mode to nonblicking. */
err = set_nonblock_flag (server_sock, 1);
if (err)
goto out;
client = fdopen (client_sock, "r+");
if (! client)
{
err = errno;
goto out;
}
server = fdopen (server_sock, "r+");
if (! server)
{
err = errno;
goto out;
}
err = io_loop (client, server, protocol);
out:
if (client)
fclose (client);
else
close (client_sock);
if (server)
fclose (server);
else
close (server_sock);
free (data);
return NULL;
}
static int
run_proxy (void)
{
int client_sock;
int my_sock;
int err;
struct sockaddr_un clientname;
size_t size;
pthread_t mythread;
struct thread_data *thread_data;
FILE *protocol_file;
pthread_attr_t thread_attr;
protocol_file = NULL;
err = pthread_attr_init (&thread_attr);
if (err)
goto out;
err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
if (err)
goto out;
if (opt.protocol_file)
{
protocol_file = fopen (opt.protocol_file, "a");
if (! protocol_file)
{
err = errno;
goto out;
}
}
else
protocol_file = stdout;
err = create_server_socket (opt.listen_spec, &my_sock);
if (err)
goto out;
while (1)
{
/* Accept new client. */
size = sizeof (clientname);
client_sock = accept (my_sock,
(struct sockaddr *) &clientname,
&size);
if (client_sock < 0)
{
err = errno;
break;
}
/* Set IO mode to nonblicking. */
err = set_nonblock_flag (client_sock, 1);
if (err)
{
close (client_sock);
break;
}
/* Got new client -> handle in new process. */
thread_data = malloc (sizeof (*thread_data));
if (! thread_data)
{
err = errno;
break;
}
thread_data->client_sock = client_sock;
thread_data->protocol_file = protocol_file;
err = pthread_create (&mythread, &thread_attr, serve_client, thread_data);
if (err)
break;
}
if (err)
goto out;
/* ? */
out:
pthread_attr_destroy (&thread_attr);
fclose (protocol_file); /* FIXME, err checking. */
return err;
}
static int
print_help (int ret)
{
printf ("Usage: sockprox [options] "
"--server SERVER-SOCKET --listen PROXY-SOCKET\n");
exit (ret);
}
int
main (int argc, char **argv)
{
struct option long_options[] =
{
{ "help", no_argument, 0, 'h' },
{ "verbose", no_argument, &opt.verbose, 1 },
{ "protocol", required_argument, 0, 'p' },
{ "server", required_argument, 0, 's' },
{ "listen", required_argument, 0, 'l' },
{ 0, 0, 0, 0 }
};
int ret;
int err;
int c;
while (1)
{
int opt_idx = 0;
c = getopt_long (argc, argv, "hvp:s:l:",
long_options, &opt_idx);
if (c == -1)
break;
switch (c)
{
case 0:
if (long_options[opt_idx].flag)
break;
printf ("option %s", long_options[opt_idx].name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
break;
case 'p':
opt.protocol_file = optarg;
break;
case 's':
opt.server_spec = optarg;
break;
case 'l':
opt.listen_spec = optarg;
break;
case 'v':
opt.verbose = 1;
break;
case 'h':
print_help (EXIT_SUCCESS);
break;
default:
abort ();
}
}
if (opt.verbose)
{
printf ("server: %s\n", opt.server_spec ? opt.server_spec : "");
printf ("listen: %s\n", opt.listen_spec ? opt.listen_spec : "");
printf ("protocol: %s\n", opt.protocol_file ? opt.protocol_file : "");
}
if (! (opt.server_spec && opt.listen_spec))
print_help (EXIT_FAILURE);
err = run_proxy ();
if (err)
{
fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
ret = EXIT_FAILURE;
}
else
/* ? */
ret = EXIT_SUCCESS;
return ret;
}
/*
Local Variables:
compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"
End:
*/