1 /* sockprox - Proxy for local sockets with logging facilities
2 * Copyright (C) 2007 g10 Code GmbH.
4 * sockprox is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * sockprox is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 /* Hacked by Moritz Schulte <moritz@g10code.com>.
22 Run a server which binds to a local socket. For example,
23 gpg-agent. gpg-agent's local socket is specified with --server.
24 sockprox opens a new local socket (here "mysock"); the whole
25 traffic between server and client is written to "/tmp/prot" in this
28 ./sockprox --server /tmp/gpg-PKdD8r/S.gpg-agent.ssh \
29 --listen mysock --protocol /tmp/prot
31 Then, redirect your ssh-agent client to sockprox by setting
32 SSH_AUTH_SOCK to "mysock".
44 #include <sys/socket.h>
58 struct opt opt = { NULL, NULL, NULL, 0 };
69 create_server_socket (const char *filename, int *new_sock)
71 struct sockaddr_un name;
77 /* Create the socket. */
78 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
85 /* Bind a name to the socket. */
86 name.sun_family = AF_LOCAL;
87 strncpy (name.sun_path, filename, sizeof (name.sun_path));
88 name.sun_path[sizeof (name.sun_path) - 1] = '\0';
89 size = SUN_LEN (&name);
93 ret = bind (sock, (struct sockaddr *) &name, size);
100 ret = listen (sock, 2);
116 connect_to_socket (const char *filename, int *new_sock)
118 struct sockaddr_un srvr_addr;
124 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
131 memset (&srvr_addr, 0, sizeof srvr_addr);
132 srvr_addr.sun_family = AF_LOCAL;
133 strncpy (srvr_addr.sun_path, filename, sizeof (srvr_addr.sun_path) - 1);
134 srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
135 len = SUN_LEN (&srvr_addr);
137 ret = connect (sock, (struct sockaddr *) &srvr_addr, len);
156 log_data (unsigned char *data, size_t length,
157 FILE *from, FILE *to, FILE *protocol)
163 flockfile (protocol);
164 fprintf (protocol, "%i -> %i: ", fileno (from), fileno (to));
165 for (i = 0; i < length; i++)
166 fprintf (protocol, "%02X", data[i]);
167 fprintf (protocol, "\n");
168 funlockfile (protocol);
170 ret = fflush (protocol);
180 transfer_data (FILE *from, FILE *to, FILE *protocol)
182 unsigned char buffer[BUFSIZ];
191 len = fread (buffer, 1, sizeof (buffer), from);
195 err = log_data (buffer, len, from, to, protocol);
199 written = fwrite (buffer, 1, len, to);
222 io_loop (FILE *client, FILE *server, FILE *protocol)
224 fd_set active_fd_set, read_fd_set;
228 FD_ZERO (&active_fd_set);
229 FD_SET (fileno (client), &active_fd_set);
230 FD_SET (fileno (server), &active_fd_set);
236 read_fd_set = active_fd_set;
240 ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
247 if (FD_ISSET (fileno (client), &read_fd_set))
252 /* Forward data from client to server. */
253 err = transfer_data (client, server, protocol);
255 else if (FD_ISSET (fileno (server), &read_fd_set))
260 /* Forward data from server to client. */
261 err = transfer_data (server, client, protocol);
274 /* Set the 'O_NONBLOCK' flag of DESC if VALUE is nonzero,
275 or clear the flag if VALUE is 0.
276 Return 0 on success, or -1 on error with 'errno' set. */
279 set_nonblock_flag (int desc, int value)
281 int oldflags = fcntl (desc, F_GETFL, 0);
285 /* If reading the flags failed, return error indication now. */
288 /* Set just the flag we want to set. */
290 oldflags |= O_NONBLOCK;
292 oldflags &= ~O_NONBLOCK;
293 /* Store modified flag word in the descriptor. */
295 ret = fcntl (desc, F_SETFL, oldflags);
307 serve_client (void *data)
309 struct thread_data *thread_data = data;
310 int client_sock = thread_data->client_sock;
312 FILE *protocol = thread_data->protocol_file;
320 /* Connect to server. */
321 err = connect_to_socket (opt.server_spec, &server_sock);
325 /* Set IO mode to nonblicking. */
326 err = set_nonblock_flag (server_sock, 1);
330 client = fdopen (client_sock, "r+");
337 server = fdopen (server_sock, "r+");
344 err = io_loop (client, server, protocol);
369 struct sockaddr_un clientname;
372 struct thread_data *thread_data;
374 pthread_attr_t thread_attr;
376 protocol_file = NULL;
378 err = pthread_attr_init (&thread_attr);
382 err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
386 if (opt.protocol_file)
388 protocol_file = fopen (opt.protocol_file, "a");
396 protocol_file = stdout;
398 err = create_server_socket (opt.listen_spec, &my_sock);
404 /* Accept new client. */
405 size = sizeof (clientname);
406 client_sock = accept (my_sock,
407 (struct sockaddr *) &clientname,
415 /* Set IO mode to nonblicking. */
416 err = set_nonblock_flag (client_sock, 1);
423 /* Got new client -> handle in new process. */
425 thread_data = malloc (sizeof (*thread_data));
431 thread_data->client_sock = client_sock;
432 thread_data->protocol_file = protocol_file;
434 err = pthread_create (&mythread, &thread_attr, serve_client, thread_data);
445 pthread_attr_destroy (&thread_attr);
447 fclose (protocol_file); /* FIXME, err checking. */
457 printf ("Usage: sockprox [options] "
458 "--server SERVER-SOCKET --listen PROXY-SOCKET\n");
463 main (int argc, char **argv)
465 struct option long_options[] =
467 { "help", no_argument, 0, 'h' },
468 { "verbose", no_argument, &opt.verbose, 1 },
469 { "protocol", required_argument, 0, 'p' },
470 { "server", required_argument, 0, 's' },
471 { "listen", required_argument, 0, 'l' },
481 c = getopt_long (argc, argv, "hvp:s:l:",
482 long_options, &opt_idx);
490 if (long_options[opt_idx].flag)
492 printf ("option %s", long_options[opt_idx].name);
494 printf (" with arg %s", optarg);
499 opt.protocol_file = optarg;
503 opt.server_spec = optarg;
507 opt.listen_spec = optarg;
515 print_help (EXIT_SUCCESS);
525 printf ("server: %s\n", opt.server_spec ? opt.server_spec : "");
526 printf ("listen: %s\n", opt.listen_spec ? opt.listen_spec : "");
527 printf ("protocol: %s\n", opt.protocol_file ? opt.protocol_file : "");
530 if (! (opt.server_spec && opt.listen_spec))
531 print_help (EXIT_FAILURE);
536 fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
549 compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"