mirror of
git://git.gnupg.org/gnupg.git
synced 2024-09-06 12:46:22 +02:00
![Werner Koch](/assets/img/avatar_default.png)
We better do this once and for all instead of cluttering all future commits with diffs of trailing white spaces. In the majority of cases blank or single lines are affected and thus this change won't disturb a git blame too much. For future commits the pre-commit scripts checks that this won't happen again.
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:
|
||
*/
|