A whole bunch of changes to allow building for Windows.
[gnupg.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 2 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, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 /* Hacked by Moritz Schulte <moritz@g10code.com>. 
21
22    Usage example:
23
24    Run a server which binds to a local socket.  For example,
25    gpg-agent.  gpg-agent's local socket is specified with --server.
26    sockprox opens a new local socket (here "mysock"); the whole
27    traffic between server and client is written to "/tmp/prot" in this
28    case.
29   
30      ./sockprox --server /tmp/gpg-PKdD8r/S.gpg-agent.ssh \
31                 --listen mysock --protocol /tmp/prot
32   
33    Then, redirect your ssh-agent client to sockprox by setting
34    SSH_AUTH_SOCK to "mysock".
35 */
36
37
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <getopt.h>
43 #include <stddef.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <sys/socket.h>
47 #include <sys/un.h>
48 #include <fcntl.h>
49 #include <assert.h>
50 #include <pthread.h>
51
52 struct opt
53 {
54   char *protocol_file;
55   char *server_spec;
56   char *listen_spec;
57   int verbose;
58 };
59
60 struct opt opt = { NULL, NULL, NULL, 0 };
61
62 struct thread_data
63 {
64   int client_sock;
65   FILE *protocol_file;
66 };
67
68 \f
69
70 static int
71 create_server_socket (const char *filename, int *new_sock)
72 {
73   struct sockaddr_un name;
74   size_t size;
75   int sock;
76   int ret;
77   int err;
78
79   /* Create the socket. */
80   sock = socket (PF_LOCAL, SOCK_STREAM, 0);
81   if (sock < 0)
82     {
83       err = errno;
84       goto out;
85     }
86
87   /* Bind a name to the socket. */
88   name.sun_family = AF_LOCAL;
89   strncpy (name.sun_path, filename, sizeof (name.sun_path));
90   name.sun_path[sizeof (name.sun_path) - 1] = '\0';
91
92   size = SUN_LEN (&name);
93
94   remove (filename);
95
96   ret = bind (sock, (struct sockaddr *) &name, size);
97   if (ret < 0)
98     {
99       err = errno;
100       goto out;
101     }
102
103   ret = listen (sock, 2);
104   if (ret < 0)
105     {
106       err = errno;
107       goto out;
108     }
109
110   *new_sock = sock;
111   err = 0;
112
113  out:
114
115   return err;
116 }
117
118 static int
119 connect_to_socket (const char *filename, int *new_sock)
120 {
121   struct sockaddr_un srvr_addr;
122   size_t len;
123   int sock;
124   int ret;
125   int err;
126
127   sock = socket (PF_LOCAL, SOCK_STREAM, 0);
128   if (sock == -1)
129     {
130       err = errno;
131       goto out;
132     }
133
134   memset (&srvr_addr, 0, sizeof srvr_addr);
135   srvr_addr.sun_family = AF_LOCAL;
136   strncpy (srvr_addr.sun_path, filename, sizeof (srvr_addr.sun_path) - 1);
137   srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
138   len = SUN_LEN (&srvr_addr);
139
140   ret = connect (sock, (struct sockaddr *) &srvr_addr, len);
141   if (ret == -1)
142     {
143       close (sock);
144       err = errno;
145       goto out;
146     }
147
148   *new_sock = sock;
149   err = 0;
150
151  out:
152
153   return err;
154 }
155
156 \f
157
158 static int
159 log_data (unsigned char *data, size_t length,
160           FILE *from, FILE *to, FILE *protocol)
161 {
162   unsigned int i;
163   int ret;
164   int err;
165
166   flockfile (protocol);
167   fprintf (protocol, "%i -> %i: ", fileno (from), fileno (to));
168   for (i = 0; i < length; i++)
169     fprintf (protocol, "%02X", data[i]);
170   fprintf (protocol, "\n");
171   funlockfile (protocol);
172
173   ret = fflush (protocol);
174   if (ret == EOF)
175     err = errno;
176   else
177     err = 0;
178
179   return err;
180 }
181
182 static int
183 transfer_data (FILE *from, FILE *to, FILE *protocol)
184 {
185   unsigned char buffer[BUFSIZ];
186   size_t len, written;
187   int err;
188   int ret;
189
190   err = 0;
191
192   while (1)
193     {
194       len = fread (buffer, 1, sizeof (buffer), from);
195       if (len == 0)
196         break;
197
198       err = log_data (buffer, len, from, to, protocol);
199       if (err)
200         break;
201
202       written = fwrite (buffer, 1, len, to);
203       if (written != len)
204         {
205           err = errno;
206           break;
207         }
208
209       ret = fflush (to);
210       if (ret == EOF)
211         {
212           err = errno;
213           break;
214         }
215
216       if (ferror (from))
217         break;
218     }
219
220   return err;
221 }
222
223
224 static int
225 io_loop (FILE *client, FILE *server, FILE *protocol)
226 {
227   fd_set active_fd_set, read_fd_set;
228   int ret;
229   int err;
230
231   FD_ZERO (&active_fd_set);
232   FD_SET (fileno (client), &active_fd_set);
233   FD_SET (fileno (server), &active_fd_set);
234
235   err = 0;
236
237   while (1)
238     {
239       read_fd_set = active_fd_set;
240
241       /* FIXME: eof?  */
242
243       ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
244       if (ret < 0)
245         {
246           err = errno;
247           break;
248         }
249
250       if (FD_ISSET (fileno (client), &read_fd_set))
251         {
252           if (feof (client))
253             break;
254
255           /* Forward data from client to server.  */
256           err = transfer_data (client, server, protocol);
257         }
258       else if (FD_ISSET (fileno (server), &read_fd_set))
259         {
260           if (feof (server))
261             break;
262
263           /* Forward data from server to client.  */
264           err = transfer_data (server, client, protocol);
265         }
266
267       if (err)
268         break;
269     }
270
271   return err;
272 }
273
274 \f
275
276
277 /* Set the `O_NONBLOCK' flag of DESC if VALUE is nonzero,
278    or clear the flag if VALUE is 0.
279    Return 0 on success, or -1 on error with `errno' set. */
280
281 int
282 set_nonblock_flag (int desc, int value)
283 {
284   int oldflags = fcntl (desc, F_GETFL, 0);
285   int err;
286   int ret;
287
288   /* If reading the flags failed, return error indication now. */
289   if (oldflags == -1)
290     return -1;
291   /* Set just the flag we want to set. */
292   if (value != 0)
293     oldflags |= O_NONBLOCK;
294   else
295     oldflags &= ~O_NONBLOCK;
296   /* Store modified flag word in the descriptor. */
297
298   ret = fcntl (desc, F_SETFL, oldflags);
299   if (ret == -1)
300     err = errno;
301   else
302     err = 0;
303
304   return err;
305 }
306
307 \f
308
309 void *
310 serve_client (void *data)
311 {
312   struct thread_data *thread_data = data;
313   int client_sock = thread_data->client_sock;
314   int server_sock;
315   FILE *protocol = thread_data->protocol_file;
316   FILE *client;
317   FILE *server;
318   int err;
319
320   client = NULL;
321   server = NULL;
322
323   /* Connect to server.  */
324   err = connect_to_socket (opt.server_spec, &server_sock);
325   if (err)
326     goto out;
327
328   /* Set IO mode to nonblicking.  */
329   err = set_nonblock_flag (server_sock, 1);
330   if (err)
331     goto out;
332
333   client = fdopen (client_sock, "r+");
334   if (! client)
335     {
336       err = errno;
337       goto out;
338     }
339
340   server = fdopen (server_sock, "r+");
341   if (! server)
342     {
343       err = errno;
344       goto out;
345     }
346
347   err = io_loop (client, server, protocol);
348
349  out:
350
351   if (client)
352     fclose (client);
353   else
354     close (client_sock);
355
356   if (server)
357     fclose (server);
358   else
359     close (server_sock);
360
361   free (data);
362
363   return NULL;
364 }
365
366 static int
367 run_proxy (void)
368 {
369   int client_sock;
370   int my_sock;
371   int err;
372   struct sockaddr_un clientname;
373   size_t size;
374   pthread_t  mythread;
375   struct thread_data *thread_data;
376   FILE *protocol_file;
377   pthread_attr_t thread_attr;
378
379   protocol_file = NULL;
380
381   err = pthread_attr_init (&thread_attr);
382   if (err)
383     goto out;
384
385   err = pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED);
386   if (err)
387     goto out;
388
389   if (opt.protocol_file)
390     {
391       protocol_file = fopen (opt.protocol_file, "a");
392       if (! protocol_file)
393         {
394           err = errno;
395           goto out;
396         }
397     }
398   else
399     protocol_file = stdout;
400
401   err = create_server_socket (opt.listen_spec, &my_sock);
402   if (err)
403     goto out;
404
405   while (1)
406     {
407       /* Accept new client.  */
408       size = sizeof (clientname);
409       client_sock = accept (my_sock,
410                             (struct sockaddr *) &clientname,
411                             &size);
412       if (client_sock < 0)
413         {
414           err = errno;
415           break;
416         }
417
418       /* Set IO mode to nonblicking.  */
419       err = set_nonblock_flag (client_sock, 1);
420       if (err)
421         {
422           close (client_sock);
423           break;
424         }
425
426       /* Got new client -> handle in new process.  */
427
428       thread_data = malloc (sizeof (*thread_data));
429       if (! thread_data)
430         {
431           err = errno;
432           break;
433         }
434       thread_data->client_sock = client_sock;
435       thread_data->protocol_file = protocol_file;
436
437       err = pthread_create (&mythread, &thread_attr, serve_client, thread_data);
438       if (err)
439         break;
440     }
441   if (err)
442     goto out;
443
444   /* ? */
445
446  out:
447   
448   pthread_attr_destroy (&thread_attr);
449   fclose (protocol_file);       /* FIXME, err checking.  */
450
451   return err;
452 }
453
454 \f
455
456 static int
457 print_help (int ret)
458 {
459   printf ("Usage: sockprox [options] "
460           "--server SERVER-SOCKET --listen PROXY-SOCKET\n");
461   exit (ret);
462 }
463
464 int
465 main (int argc, char **argv)
466 {
467   struct option long_options[] =
468     {
469       { "help",     no_argument,       0,            'h' },
470       { "verbose",  no_argument,       &opt.verbose, 1   },
471       { "protocol", required_argument, 0,            'p' },
472       { "server",   required_argument, 0,            's' },
473       { "listen",   required_argument, 0,            'l' },
474       { 0, 0, 0, 0 }
475     };
476   int ret;
477   int err;
478   int c;
479
480   while (1)
481     {
482       int opt_idx = 0;
483       c = getopt_long (argc, argv, "hvp:s:l:",
484                        long_options, &opt_idx);
485
486       if (c == -1)
487         break;
488
489       switch (c)
490         {
491         case 0:
492           if (long_options[opt_idx].flag)
493             break;
494           printf ("option %s", long_options[opt_idx].name);
495           if (optarg)
496             printf (" with arg %s", optarg);
497           printf ("\n");
498           break;
499
500         case 'p':
501           opt.protocol_file = optarg;
502           break;
503
504         case 's':
505           opt.server_spec = optarg;
506           break;
507
508         case 'l':
509           opt.listen_spec = optarg;
510           break;
511
512         case 'v':
513           opt.verbose = 1;
514           break;
515
516         case 'h':
517           print_help (EXIT_SUCCESS);
518           break;
519
520         default:
521           abort ();
522         }
523     }
524
525   if (opt.verbose)
526     {
527       printf ("server: %s\n", opt.server_spec ? opt.server_spec : "");
528       printf ("listen: %s\n", opt.listen_spec ? opt.listen_spec : "");
529       printf ("protocol: %s\n", opt.protocol_file ? opt.protocol_file : "");
530     }
531
532   if (! (opt.server_spec && opt.listen_spec))
533     print_help (EXIT_FAILURE);
534
535   err = run_proxy ();
536   if (err)
537     {
538       fprintf (stderr, "run_proxy() failed: %s\n", strerror (err));
539       ret = EXIT_FAILURE;
540     }
541   else
542     /* ? */
543     ret = EXIT_SUCCESS;
544
545   return ret;
546 }
547
548
549 /*
550 Local Variables:
551 compile-command: "cc -Wall -g -o sockprox sockprox.c -lpthread"
552 End:
553 */