summaryrefslogtreecommitdiffstats
path: root/tools/sockprox.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/sockprox.c')
-rw-r--r--tools/sockprox.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/tools/sockprox.c b/tools/sockprox.c
new file mode 100644
index 0000000..8648bb5
--- /dev/null
+++ b/tools/sockprox.c
@@ -0,0 +1,551 @@
+/* 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 <https://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);
+ if (protocol_file)
+ 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:
+*/