chiark / gitweb /
Import gnupg2_2.1.17.orig.tar.bz2
[gnupg2.git] / tools / sockprox.c
1 /* sockprox - Proxy for local sockets with logging facilities
2  *      Copyright (C) 2007 g10 Code GmbH.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 /* Hacked by Moritz Schulte <moritz@g10code.com>.
19
20    Usage example:
21
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.
27
28      ./sockprox --server /tmp/gpg-PKdD8r/S.gpg-agent.ssh \
29                 --listen mysock --protocol /tmp/prot
30
31    Then, redirect your ssh-agent client to sockprox by setting
32    SSH_AUTH_SOCK to "mysock".
33 */
34
35
36
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>
49
50 struct opt
51 {
52   char *protocol_file;
53   char *server_spec;
54   char *listen_spec;
55   int verbose;
56 };
57
58 struct opt opt = { NULL, NULL, NULL, 0 };
59
60 struct thread_data
61 {
62   int client_sock;
63   FILE *protocol_file;
64 };
65
66 \f
67
68 static int
69 create_server_socket (const char *filename, int *new_sock)
70 {
71   struct sockaddr_un name;
72   size_t size;
73   int sock;
74   int ret;
75   int err;
76
77   /* Create the socket. */
78   sock = socket (PF_LOCAL, SOCK_STREAM, 0);
79   if (sock < 0)
80     {
81       err = errno;
82       goto out;
83     }
84
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);
90
91   remove (filename);
92
93   ret = bind (sock, (struct sockaddr *) &name, size);
94   if (ret < 0)
95     {
96       err = errno;
97       goto out;
98     }
99
100   ret = listen (sock, 2);
101   if (ret < 0)
102     {
103       err = errno;
104       goto out;
105     }
106
107   *new_sock = sock;
108   err = 0;
109
110  out:
111
112   return err;
113 }
114
115 static int
116 connect_to_socket (const char *filename, int *new_sock)
117 {
118   struct sockaddr_un srvr_addr;
119   size_t len;
120   int sock;
121   int ret;
122   int err;
123
124   sock = socket (PF_LOCAL, SOCK_STREAM, 0);
125   if (sock == -1)
126     {
127       err = errno;
128       goto out;
129     }
130
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);
136
137   ret = connect (sock, (struct sockaddr *) &srvr_addr, len);
138   if (ret == -1)
139     {
140       close (sock);
141       err = errno;
142       goto out;
143     }
144
145   *new_sock = sock;
146   err = 0;
147
148  out:
149
150   return err;
151 }
152
153 \f
154
155 static int
156 log_data (unsigned char *data, size_t length,
157           FILE *from, FILE *to, FILE *protocol)
158 {
159   unsigned int i;
160   int ret;
161   int err;
162
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);
169
170   ret = fflush (protocol);
171   if (ret == EOF)
172     err = errno;
173   else
174     err = 0;
175
176   return err;
177 }
178
179 static int
180 transfer_data (FILE *from, FILE *to, FILE *protocol)
181 {
182   unsigned char buffer[BUFSIZ];
183   size_t len, written;
184   int err;
185   int ret;
186
187   err = 0;
188
189   while (1)
190     {
191       len = fread (buffer, 1, sizeof (buffer), from);
192       if (len == 0)
193         break;
194
195       err = log_data (buffer, len, from, to, protocol);
196       if (err)
197         break;
198
199       written = fwrite (buffer, 1, len, to);
200       if (written != len)
201         {
202           err = errno;
203           break;
204         }
205
206       ret = fflush (to);
207       if (ret == EOF)
208         {
209           err = errno;
210           break;
211         }
212
213       if (ferror (from))
214         break;
215     }
216
217   return err;
218 }
219
220
221 static int
222 io_loop (FILE *client, FILE *server, FILE *protocol)
223 {
224   fd_set active_fd_set, read_fd_set;
225   int ret;
226   int err;
227
228   FD_ZERO (&active_fd_set);
229   FD_SET (fileno (client), &active_fd_set);
230   FD_SET (fileno (server), &active_fd_set);
231
232   err = 0;
233
234   while (1)
235     {
236       read_fd_set = active_fd_set;
237
238       /* FIXME: eof?  */
239
240       ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
241       if (ret < 0)
242         {
243           err = errno;
244           break;
245         }
246
247       if (FD_ISSET (fileno (client), &read_fd_set))
248         {
249           if (feof (client))
250             break;
251
252           /* Forward data from client to server.  */
253           err = transfer_data (client, server, protocol);
254         }
255       else if (FD_ISSET (fileno (server), &read_fd_set))
256         {
257           if (feof (server))
258             break;
259
260           /* Forward data from server to client.  */
261           err = transfer_data (server, client, protocol);
262         }
263
264       if (err)
265         break;
266     }
267
268   return err;
269 }
270
271 \f
272
273
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. */
277
278 int
279 set_nonblock_flag (int desc, int value)
280 {
281   int oldflags = fcntl (desc, F_GETFL, 0);
282   int err;
283   int ret;
284
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. */
294
295   ret = fcntl (desc, F_SETFL, oldflags);
296   if (ret == -1)
297     err = errno;
298   else
299     err = 0;
300
301   return err;
302 }
303
304 \f
305
306 void *
307 serve_client (void *data)
308 {
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;
316
317   client = NULL;
318   server = NULL;
319
320   /* Connect to server.  */
321   err = connect_to_socket (opt.server_spec, &server_sock);
322   if (err)
323     goto out;
324
325   /* Set IO mode to nonblicking.  */
326   err = set_nonblock_flag (server_sock, 1);
327   if (err)
328     goto out;
329
330   client = fdopen (client_sock, "r+");
331   if (! client)
332     {
333       err = errno;
334       goto out;
335     }
336
337   server = fdopen (server_sock, "r+");
338   if (! server)
339     {
340       err = errno;
341       goto out;
342     }
343
344   err = io_loop (client, server, protocol);
345
346  out:
347
348   if (client)
349     fclose (client);
350   else
351     close (client_sock);
352
353   if (server)
354     fclose (server);
355   else
356     close (server_sock);
357
358   free (data);
359
360   return NULL;
361 }
362
363 static int
364 run_proxy (void)
365 {
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;
375
376   protocol_file = NULL;
377
378   err = pthread_attr_init (&thread_attr);
379   if (err)
380     goto out;
381
382   err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
383   if (err)
384     goto out;
385
386   if (opt.protocol_file)
387     {
388       protocol_file = fopen (opt.protocol_file, "a");
389       if (! protocol_file)
390         {
391           err = errno;
392           goto out;
393         }
394     }
395   else
396     protocol_file = stdout;
397
398   err = create_server_socket (opt.listen_spec, &my_sock);
399   if (err)
400     goto out;
401
402   while (1)
403     {
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)
410         {
411           err = errno;
412           break;
413         }
414
415       /* Set IO mode to nonblicking.  */
416       err = set_nonblock_flag (client_sock, 1);
417       if (err)
418         {
419           close (client_sock);
420           break;
421         }
422
423       /* Got new client -> handle in new process.  */
424
425       thread_data = malloc (sizeof (*thread_data));
426       if (! thread_data)
427         {
428           err = errno;
429           break;
430         }
431       thread_data->client_sock = client_sock;
432       thread_data->protocol_file = protocol_file;
433
434       err = pthread_create (&mythread, &thread_attr, serve_client, thread_data);
435       if (err)
436         break;
437     }
438   if (err)
439     goto out;
440
441   /* ? */
442
443  out:
444
445   pthread_attr_destroy (&thread_attr);
446   if (protocol_file)
447     fclose (protocol_file);     /* FIXME, err checking.  */
448
449   return err;
450 }
451
452 \f
453
454 static int
455 print_help (int ret)
456 {
457   printf ("Usage: sockprox [options] "
458           "--server SERVER-SOCKET --listen PROXY-SOCKET\n");
459   exit (ret);
460 }
461
462 int
463 main (int argc, char **argv)
464 {
465   struct option long_options[] =
466     {
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 }
473     };
474   int ret;
475   int err;
476   int c;
477
478   while (1)
479     {
480       int opt_idx = 0;
481       c = getopt_long (argc, argv, "hvp:s:l:",
482                        long_options, &opt_idx);
483
484       if (c == -1)
485         break;
486
487       switch (c)
488         {
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;
497
498         case 'p':
499           opt.protocol_file = optarg;
500           break;
501
502         case 's':
503           opt.server_spec = optarg;
504           break;
505
506         case 'l':
507           opt.listen_spec = optarg;
508           break;
509
510         case 'v':
511           opt.verbose = 1;
512           break;
513
514         case 'h':
515           print_help (EXIT_SUCCESS);
516           break;
517
518         default:
519           abort ();
520         }
521     }
522
523   if (opt.verbose)
524     {
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 : "");
528     }
529
530   if (! (opt.server_spec && opt.listen_spec))
531     print_help (EXIT_FAILURE);
532
533   err = run_proxy ();
534   if (err)
535     {
536       fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
537       ret = EXIT_FAILURE;
538     }
539   else
540     /* ? */
541     ret = EXIT_SUCCESS;
542
543   return ret;
544 }
545
546
547 /*
548 Local Variables:
549 compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"
550 End:
551 */