diff options
author | Werner Koch <wk@gnupg.org> | 2007-05-09 13:01:33 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2007-05-09 13:01:33 +0200 |
commit | edb3dc99e9833136a7454561d8e0845aa9e55609 (patch) | |
tree | 112160d61c00df7b476409a698f746b07076e20c /tools/sockprox.c | |
parent | Add a howto section. (diff) | |
download | gnupg2-edb3dc99e9833136a7454561d8e0845aa9e55609.tar.xz gnupg2-edb3dc99e9833136a7454561d8e0845aa9e55609.zip |
Preparing 2.0.4gnupg-2.0.4
Diffstat (limited to '')
-rw-r--r-- | tools/sockprox.c | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/tools/sockprox.c b/tools/sockprox.c new file mode 100644 index 000000000..171facaa2 --- /dev/null +++ b/tools/sockprox.c @@ -0,0 +1,553 @@ +/* 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* 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: +*/ |