po/
[gnupg.git] / tools / sockprox.c
blobfe8d320a1b143c6f61002cadc6a1f13c6fcc98fc
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';
89 size = SUN_LEN (&name);
91 remove (filename);
93 ret = bind (sock, (struct sockaddr *) &name, size);
94 if (ret < 0)
96 err = errno;
97 goto out;
100 ret = listen (sock, 2);
101 if (ret < 0)
103 err = errno;
104 goto out;
107 *new_sock = sock;
108 err = 0;
110 out:
112 return err;
115 static int
116 connect_to_socket (const char *filename, int *new_sock)
118 struct sockaddr_un srvr_addr;
119 size_t len;
120 int sock;
121 int ret;
122 int err;
124 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
125 if (sock == -1)
127 err = errno;
128 goto out;
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);
138 if (ret == -1)
140 close (sock);
141 err = errno;
142 goto out;
145 *new_sock = sock;
146 err = 0;
148 out:
150 return err;
155 static int
156 log_data (unsigned char *data, size_t length,
157 FILE *from, FILE *to, FILE *protocol)
159 unsigned int i;
160 int ret;
161 int err;
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);
171 if (ret == EOF)
172 err = errno;
173 else
174 err = 0;
176 return err;
179 static int
180 transfer_data (FILE *from, FILE *to, FILE *protocol)
182 unsigned char buffer[BUFSIZ];
183 size_t len, written;
184 int err;
185 int ret;
187 err = 0;
189 while (1)
191 len = fread (buffer, 1, sizeof (buffer), from);
192 if (len == 0)
193 break;
195 err = log_data (buffer, len, from, to, protocol);
196 if (err)
197 break;
199 written = fwrite (buffer, 1, len, to);
200 if (written != len)
202 err = errno;
203 break;
206 ret = fflush (to);
207 if (ret == EOF)
209 err = errno;
210 break;
213 if (ferror (from))
214 break;
217 return err;
221 static int
222 io_loop (FILE *client, FILE *server, FILE *protocol)
224 fd_set active_fd_set, read_fd_set;
225 int ret;
226 int err;
228 FD_ZERO (&active_fd_set);
229 FD_SET (fileno (client), &active_fd_set);
230 FD_SET (fileno (server), &active_fd_set);
232 err = 0;
234 while (1)
236 read_fd_set = active_fd_set;
238 /* FIXME: eof? */
240 ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
241 if (ret < 0)
243 err = errno;
244 break;
247 if (FD_ISSET (fileno (client), &read_fd_set))
249 if (feof (client))
250 break;
252 /* Forward data from client to server. */
253 err = transfer_data (client, server, protocol);
255 else if (FD_ISSET (fileno (server), &read_fd_set))
257 if (feof (server))
258 break;
260 /* Forward data from server to client. */
261 err = transfer_data (server, client, protocol);
264 if (err)
265 break;
268 return err;
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);
282 int err;
283 int ret;
285 /* If reading the flags failed, return error indication now. */
286 if (oldflags == -1)
287 return -1;
288 /* Set just the flag we want to set. */
289 if (value != 0)
290 oldflags |= O_NONBLOCK;
291 else
292 oldflags &= ~O_NONBLOCK;
293 /* Store modified flag word in the descriptor. */
295 ret = fcntl (desc, F_SETFL, oldflags);
296 if (ret == -1)
297 err = errno;
298 else
299 err = 0;
301 return err;
306 void *
307 serve_client (void *data)
309 struct thread_data *thread_data = data;
310 int client_sock = thread_data->client_sock;
311 int server_sock;
312 FILE *protocol = thread_data->protocol_file;
313 FILE *client;
314 FILE *server;
315 int err;
317 client = NULL;
318 server = NULL;
320 /* Connect to server. */
321 err = connect_to_socket (opt.server_spec, &server_sock);
322 if (err)
323 goto out;
325 /* Set IO mode to nonblicking. */
326 err = set_nonblock_flag (server_sock, 1);
327 if (err)
328 goto out;
330 client = fdopen (client_sock, "r+");
331 if (! client)
333 err = errno;
334 goto out;
337 server = fdopen (server_sock, "r+");
338 if (! server)
340 err = errno;
341 goto out;
344 err = io_loop (client, server, protocol);
346 out:
348 if (client)
349 fclose (client);
350 else
351 close (client_sock);
353 if (server)
354 fclose (server);
355 else
356 close (server_sock);
358 free (data);
360 return NULL;
363 static int
364 run_proxy (void)
366 int client_sock;
367 int my_sock;
368 int err;
369 struct sockaddr_un clientname;
370 size_t size;
371 pthread_t mythread;
372 struct thread_data *thread_data;
373 FILE *protocol_file;
374 pthread_attr_t thread_attr;
376 protocol_file = NULL;
378 err = pthread_attr_init (&thread_attr);
379 if (err)
380 goto out;
382 err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
383 if (err)
384 goto out;
386 if (opt.protocol_file)
388 protocol_file = fopen (opt.protocol_file, "a");
389 if (! protocol_file)
391 err = errno;
392 goto out;
395 else
396 protocol_file = stdout;
398 err = create_server_socket (opt.listen_spec, &my_sock);
399 if (err)
400 goto out;
402 while (1)
404 /* Accept new client. */
405 size = sizeof (clientname);
406 client_sock = accept (my_sock,
407 (struct sockaddr *) &clientname,
408 &size);
409 if (client_sock < 0)
411 err = errno;
412 break;
415 /* Set IO mode to nonblicking. */
416 err = set_nonblock_flag (client_sock, 1);
417 if (err)
419 close (client_sock);
420 break;
423 /* Got new client -> handle in new process. */
425 thread_data = malloc (sizeof (*thread_data));
426 if (! thread_data)
428 err = errno;
429 break;
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);
435 if (err)
436 break;
438 if (err)
439 goto out;
441 /* ? */
443 out:
445 pthread_attr_destroy (&thread_attr);
446 fclose (protocol_file); /* FIXME, err checking. */
448 return err;
453 static int
454 print_help (int ret)
456 printf ("Usage: sockprox [options] "
457 "--server SERVER-SOCKET --listen PROXY-SOCKET\n");
458 exit (ret);
462 main (int argc, char **argv)
464 struct option long_options[] =
466 { "help", no_argument, 0, 'h' },
467 { "verbose", no_argument, &opt.verbose, 1 },
468 { "protocol", required_argument, 0, 'p' },
469 { "server", required_argument, 0, 's' },
470 { "listen", required_argument, 0, 'l' },
471 { 0, 0, 0, 0 }
473 int ret;
474 int err;
475 int c;
477 while (1)
479 int opt_idx = 0;
480 c = getopt_long (argc, argv, "hvp:s:l:",
481 long_options, &opt_idx);
483 if (c == -1)
484 break;
486 switch (c)
488 case 0:
489 if (long_options[opt_idx].flag)
490 break;
491 printf ("option %s", long_options[opt_idx].name);
492 if (optarg)
493 printf (" with arg %s", optarg);
494 printf ("\n");
495 break;
497 case 'p':
498 opt.protocol_file = optarg;
499 break;
501 case 's':
502 opt.server_spec = optarg;
503 break;
505 case 'l':
506 opt.listen_spec = optarg;
507 break;
509 case 'v':
510 opt.verbose = 1;
511 break;
513 case 'h':
514 print_help (EXIT_SUCCESS);
515 break;
517 default:
518 abort ();
522 if (opt.verbose)
524 printf ("server: %s\n", opt.server_spec ? opt.server_spec : "");
525 printf ("listen: %s\n", opt.listen_spec ? opt.listen_spec : "");
526 printf ("protocol: %s\n", opt.protocol_file ? opt.protocol_file : "");
529 if (! (opt.server_spec && opt.listen_spec))
530 print_help (EXIT_FAILURE);
532 err = run_proxy ();
533 if (err)
535 fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
536 ret = EXIT_FAILURE;
538 else
539 /* ? */
540 ret = EXIT_SUCCESS;
542 return ret;
547 Local Variables:
548 compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"
549 End: