mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
096e7457ec
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.
551 lines
10 KiB
C
551 lines
10 KiB
C
/* 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:
|
||
*/
|