Fix bug#998.
[gnupg.git] / tools / sockprox.c
blob485b37a373c073f03551f3df5c1271c462142d09
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 <http://www.gnu.org/licenses/>.
18 /* Hacked by Moritz Schulte <moritz@g10code.com>.
20 Usage example:
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
26 case.
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".
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <getopt.h>
41 #include <stddef.h>
42 #include <errno.h>
43 #include <string.h>
44 #include <sys/socket.h>
45 #include <sys/un.h>
46 #include <fcntl.h>
47 #include <assert.h>
48 #include <pthread.h>
50 struct opt
52 char *protocol_file;
53 char *server_spec;
54 char *listen_spec;
55 int verbose;
58 struct opt opt = { NULL, NULL, NULL, 0 };
60 struct thread_data
62 int client_sock;
63 FILE *protocol_file;
68 static int
69 create_server_socket (const char *filename, int *new_sock)
71 struct sockaddr_un name;
72 size_t size;
73 int sock;
74 int ret;
75 int err;
77 /* Create the socket. */
78 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
79 if (sock < 0)
81 err = errno;
82 goto out;
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';
90 size = SUN_LEN (&name);
92 remove (filename);
94 ret = bind (sock, (struct sockaddr *) &name, size);
95 if (ret < 0)
97 err = errno;
98 goto out;
101 ret = listen (sock, 2);
102 if (ret < 0)
104 err = errno;
105 goto out;
108 *new_sock = sock;
109 err = 0;
111 out:
113 return err;
116 static int
117 connect_to_socket (const char *filename, int *new_sock)
119 struct sockaddr_un srvr_addr;
120 size_t len;
121 int sock;
122 int ret;
123 int err;
125 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
126 if (sock == -1)
128 err = errno;
129 goto out;
132 memset (&srvr_addr, 0, sizeof srvr_addr);
133 srvr_addr.sun_family = AF_LOCAL;
134 strncpy (srvr_addr.sun_path, filename, sizeof (srvr_addr.sun_path) - 1);
135 srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
136 len = SUN_LEN (&srvr_addr);
138 ret = connect (sock, (struct sockaddr *) &srvr_addr, len);
139 if (ret == -1)
141 close (sock);
142 err = errno;
143 goto out;
146 *new_sock = sock;
147 err = 0;
149 out:
151 return err;
156 static int
157 log_data (unsigned char *data, size_t length,
158 FILE *from, FILE *to, FILE *protocol)
160 unsigned int i;
161 int ret;
162 int err;
164 flockfile (protocol);
165 fprintf (protocol, "%i -> %i: ", fileno (from), fileno (to));
166 for (i = 0; i < length; i++)
167 fprintf (protocol, "%02X", data[i]);
168 fprintf (protocol, "\n");
169 funlockfile (protocol);
171 ret = fflush (protocol);
172 if (ret == EOF)
173 err = errno;
174 else
175 err = 0;
177 return err;
180 static int
181 transfer_data (FILE *from, FILE *to, FILE *protocol)
183 unsigned char buffer[BUFSIZ];
184 size_t len, written;
185 int err;
186 int ret;
188 err = 0;
190 while (1)
192 len = fread (buffer, 1, sizeof (buffer), from);
193 if (len == 0)
194 break;
196 err = log_data (buffer, len, from, to, protocol);
197 if (err)
198 break;
200 written = fwrite (buffer, 1, len, to);
201 if (written != len)
203 err = errno;
204 break;
207 ret = fflush (to);
208 if (ret == EOF)
210 err = errno;
211 break;
214 if (ferror (from))
215 break;
218 return err;
222 static int
223 io_loop (FILE *client, FILE *server, FILE *protocol)
225 fd_set active_fd_set, read_fd_set;
226 int ret;
227 int err;
229 FD_ZERO (&active_fd_set);
230 FD_SET (fileno (client), &active_fd_set);
231 FD_SET (fileno (server), &active_fd_set);
233 err = 0;
235 while (1)
237 read_fd_set = active_fd_set;
239 /* FIXME: eof? */
241 ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
242 if (ret < 0)
244 err = errno;
245 break;
248 if (FD_ISSET (fileno (client), &read_fd_set))
250 if (feof (client))
251 break;
253 /* Forward data from client to server. */
254 err = transfer_data (client, server, protocol);
256 else if (FD_ISSET (fileno (server), &read_fd_set))
258 if (feof (server))
259 break;
261 /* Forward data from server to client. */
262 err = transfer_data (server, client, protocol);
265 if (err)
266 break;
269 return err;
275 /* Set the `O_NONBLOCK' flag of DESC if VALUE is nonzero,
276 or clear the flag if VALUE is 0.
277 Return 0 on success, or -1 on error with `errno' set. */
280 set_nonblock_flag (int desc, int value)
282 int oldflags = fcntl (desc, F_GETFL, 0);
283 int err;
284 int ret;
286 /* If reading the flags failed, return error indication now. */
287 if (oldflags == -1)
288 return -1;
289 /* Set just the flag we want to set. */
290 if (value != 0)
291 oldflags |= O_NONBLOCK;
292 else
293 oldflags &= ~O_NONBLOCK;
294 /* Store modified flag word in the descriptor. */
296 ret = fcntl (desc, F_SETFL, oldflags);
297 if (ret == -1)
298 err = errno;
299 else
300 err = 0;
302 return err;
307 void *
308 serve_client (void *data)
310 struct thread_data *thread_data = data;
311 int client_sock = thread_data->client_sock;
312 int server_sock;
313 FILE *protocol = thread_data->protocol_file;
314 FILE *client;
315 FILE *server;
316 int err;
318 client = NULL;
319 server = NULL;
321 /* Connect to server. */
322 err = connect_to_socket (opt.server_spec, &server_sock);
323 if (err)
324 goto out;
326 /* Set IO mode to nonblicking. */
327 err = set_nonblock_flag (server_sock, 1);
328 if (err)
329 goto out;
331 client = fdopen (client_sock, "r+");
332 if (! client)
334 err = errno;
335 goto out;
338 server = fdopen (server_sock, "r+");
339 if (! server)
341 err = errno;
342 goto out;
345 err = io_loop (client, server, protocol);
347 out:
349 if (client)
350 fclose (client);
351 else
352 close (client_sock);
354 if (server)
355 fclose (server);
356 else
357 close (server_sock);
359 free (data);
361 return NULL;
364 static int
365 run_proxy (void)
367 int client_sock;
368 int my_sock;
369 int err;
370 struct sockaddr_un clientname;
371 size_t size;
372 pthread_t mythread;
373 struct thread_data *thread_data;
374 FILE *protocol_file;
375 pthread_attr_t thread_attr;
377 protocol_file = NULL;
379 err = pthread_attr_init (&thread_attr);
380 if (err)
381 goto out;
383 err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
384 if (err)
385 goto out;
387 if (opt.protocol_file)
389 protocol_file = fopen (opt.protocol_file, "a");
390 if (! protocol_file)
392 err = errno;
393 goto out;
396 else
397 protocol_file = stdout;
399 err = create_server_socket (opt.listen_spec, &my_sock);
400 if (err)
401 goto out;
403 while (1)
405 /* Accept new client. */
406 size = sizeof (clientname);
407 client_sock = accept (my_sock,
408 (struct sockaddr *) &clientname,
409 &size);
410 if (client_sock < 0)
412 err = errno;
413 break;
416 /* Set IO mode to nonblicking. */
417 err = set_nonblock_flag (client_sock, 1);
418 if (err)
420 close (client_sock);
421 break;
424 /* Got new client -> handle in new process. */
426 thread_data = malloc (sizeof (*thread_data));
427 if (! thread_data)
429 err = errno;
430 break;
432 thread_data->client_sock = client_sock;
433 thread_data->protocol_file = protocol_file;
435 err = pthread_create (&mythread, &thread_attr, serve_client, thread_data);
436 if (err)
437 break;
439 if (err)
440 goto out;
442 /* ? */
444 out:
446 pthread_attr_destroy (&thread_attr);
447 fclose (protocol_file); /* FIXME, err checking. */
449 return err;
454 static int
455 print_help (int ret)
457 printf ("Usage: sockprox [options] "
458 "--server SERVER-SOCKET --listen PROXY-SOCKET\n");
459 exit (ret);
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' },
472 { 0, 0, 0, 0 }
474 int ret;
475 int err;
476 int c;
478 while (1)
480 int opt_idx = 0;
481 c = getopt_long (argc, argv, "hvp:s:l:",
482 long_options, &opt_idx);
484 if (c == -1)
485 break;
487 switch (c)
489 case 0:
490 if (long_options[opt_idx].flag)
491 break;
492 printf ("option %s", long_options[opt_idx].name);
493 if (optarg)
494 printf (" with arg %s", optarg);
495 printf ("\n");
496 break;
498 case 'p':
499 opt.protocol_file = optarg;
500 break;
502 case 's':
503 opt.server_spec = optarg;
504 break;
506 case 'l':
507 opt.listen_spec = optarg;
508 break;
510 case 'v':
511 opt.verbose = 1;
512 break;
514 case 'h':
515 print_help (EXIT_SUCCESS);
516 break;
518 default:
519 abort ();
523 if (opt.verbose)
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);
533 err = run_proxy ();
534 if (err)
536 fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
537 ret = EXIT_FAILURE;
539 else
540 /* ? */
541 ret = EXIT_SUCCESS;
543 return ret;
548 Local Variables:
549 compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"
550 End: