scd: Fix possible NULL deref in apdu.c
[gnupg.git] / tools / watchgnupg.c
1 /* watchgnupg.c - Socket server for GnuPG logs
2  *      Copyright (C) 2003, 2004 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stdarg.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <fcntl.h>
34 #include <time.h>
35
36 #define PGM "watchgnupg"
37
38 /* Allow for a standalone build on most systems. */
39 #ifdef VERSION
40 #define MYVERSION_LINE PGM " (GnuPG) " VERSION
41 #define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
42 #else
43 #define MYVERSION_LINE PGM
44 #define BUGREPORT_LINE ""
45 #endif
46 #if !defined(SUN_LEN) || !defined(PF_LOCAL) || !defined(AF_LOCAL)
47 #define JNLIB_NEED_AFLOCAL
48 #include "../jnlib/mischelp.h"
49 #endif
50
51
52 static int verbose;
53
54
55 static void
56 die (const char *format, ...)
57 {
58   va_list arg_ptr;
59
60   fflush (stdout);
61   fprintf (stderr, "%s: ", PGM);
62
63   va_start (arg_ptr, format);
64   vfprintf (stderr, format, arg_ptr);
65   va_end (arg_ptr);
66   putc ('\n', stderr);
67
68   exit (1);
69 }
70
71
72 /* static void */
73 /* err (const char *format, ...) */
74 /* { */
75 /*   va_list arg_ptr; */
76
77 /*   fflush (stdout); */
78 /*   fprintf (stderr, "%s: ", PGM); */
79
80 /*   va_start (arg_ptr, format); */
81 /*   vfprintf (stderr, format, arg_ptr); */
82 /*   va_end (arg_ptr); */
83 /*   putc ('\n', stderr); */
84 /* } */
85
86 static void *
87 xmalloc (size_t n)
88 {
89   void *p = malloc (n);
90   if (!p)
91     die ("out of core");
92   return p;
93 }
94
95 static void *
96 xcalloc (size_t n, size_t m)
97 {
98   void *p = calloc (n, m);
99   if (!p)
100     die ("out of core");
101   return p;
102 }
103
104 static void *
105 xrealloc (void *old, size_t n)
106 {
107   void *p = realloc (old, n);
108   if (!p)
109     die ("out of core");
110   return p;
111 }
112
113
114 struct client_s {
115   struct client_s *next;
116   int fd;
117   size_t size;  /* Allocated size of buffer. */
118   size_t len;   /* Current length of buffer. */
119   unsigned char *buffer; /* Buffer to with data already read. */
120
121 };
122 typedef struct client_s *client_t;
123
124
125
126 static void
127 print_fd_and_time (int fd)
128 {
129   struct tm *tp;
130   time_t atime = time (NULL);
131
132   tp = localtime (&atime);
133   printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
134           fd,
135           1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
136           tp->tm_hour, tp->tm_min, tp->tm_sec );
137 }
138
139
140 /* Print LINE for the client identified by C.  Calling this function
141    witgh LINE set to NULL, will flush the internal buffer. */
142 static void
143 print_line (client_t c, const char *line)
144 {
145   const char *s;
146   size_t n;
147
148   if (!line)
149     {
150       if (c->buffer && c->len)
151         {
152           print_fd_and_time (c->fd);
153           fwrite (c->buffer, c->len, 1, stdout);
154           putc ('\n', stdout);
155           c->len = 0;
156         }
157       return;
158     }
159
160   while ((s = strchr (line, '\n')))
161     {
162       print_fd_and_time (c->fd);
163       if (c->buffer && c->len)
164         {
165           fwrite (c->buffer, c->len, 1, stdout);
166           c->len = 0;
167         }
168       fwrite (line, s - line + 1, 1, stdout);
169       line = s + 1;
170     }
171   n = strlen (line);
172   if (n)
173     {
174       if (c->len + n >= c->size)
175         {
176           c->size += ((n + 255) & ~255);
177           c->buffer = (c->buffer
178                        ? xrealloc (c->buffer, c->size)
179                        : xmalloc (c->size));
180         }
181       memcpy (c->buffer + c->len, line, n);
182       c->len += n;
183     }
184 }
185
186
187 static void
188 print_version (int with_help)
189 {
190   fputs (MYVERSION_LINE "\n"
191          "Copyright (C) 2013 Free Software Foundation, Inc.\n"
192          "This program comes with ABSOLUTELY NO WARRANTY.\n"
193          "This is free software, and you are welcome to redistribute it\n"
194          "under certain conditions. See the file COPYING for details.\n",
195          stdout);
196
197   if (with_help)
198     fputs ("\n"
199           "Usage: " PGM " [OPTIONS] SOCKETNAME\n"
200           "Open the local socket SOCKETNAME and display log messages\n"
201           "\n"
202           "  --force     delete an already existing socket file\n"
203           "  --verbose   enable extra informational output\n"
204           "  --version   print version of the program and exit\n"
205           "  --help      display this help and exit\n"
206           BUGREPORT_LINE, stdout );
207
208   exit (0);
209 }
210
211 int
212 main (int argc, char **argv)
213 {
214   int last_argc = -1;
215   int force = 0;
216
217   struct sockaddr_un srvr_addr;
218   socklen_t addrlen;
219   int server;
220   int flags;
221   client_t client_list = NULL;
222
223   if (argc)
224     {
225       argc--; argv++;
226     }
227   while (argc && last_argc != argc )
228     {
229       last_argc = argc;
230       if (!strcmp (*argv, "--"))
231         {
232           argc--; argv++;
233           break;
234         }
235       else if (!strcmp (*argv, "--version"))
236         print_version (0);
237       else if (!strcmp (*argv, "--help"))
238         print_version (1);
239       else if (!strcmp (*argv, "--verbose"))
240         {
241           verbose = 1;
242           argc--; argv++;
243         }
244       else if (!strcmp (*argv, "--force"))
245         {
246           force = 1;
247           argc--; argv++;
248         }
249     }
250
251   if (argc != 1)
252     {
253       fprintf (stderr, "usage: " PGM " socketname\n");
254       exit (1);
255     }
256
257
258   if (verbose)
259     fprintf (stderr, "opening socket `%s'\n", *argv);
260
261   setvbuf (stdout, NULL, _IOLBF, 0);
262
263   server = socket (PF_LOCAL, SOCK_STREAM, 0);
264   if (server == -1)
265     die ("socket() failed: %s\n", strerror (errno));
266
267   /* We better set the listening socket to non-blocking so that we
268      don't get bitten by race conditions in accept.  The should not
269      happen for Unix Domain sockets but well, shit happens. */
270   flags = fcntl (server, F_GETFL, 0);
271   if (flags == -1)
272     die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
273   if ( fcntl (server, F_SETFL, (flags | O_NONBLOCK)) == -1)
274     die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
275
276
277   memset (&srvr_addr, 0, sizeof srvr_addr);
278   srvr_addr.sun_family = AF_LOCAL;
279   strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
280   srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
281   addrlen = SUN_LEN (&srvr_addr);
282
283
284  again:
285   if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
286     {
287       if (errno == EADDRINUSE && force)
288         {
289           force = 0;
290           remove (srvr_addr.sun_path);
291           goto again;
292         }
293       die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
294     }
295
296   if (listen (server, 5))
297     die ("listen failed: %s\n", strerror (errno));
298
299   for (;;)
300     {
301       fd_set rfds;
302       int max_fd;
303       client_t client;
304
305       /* Usually we don't have that many connections, thus it is okay
306          to set them allways from scratch and don't maintain an active
307          fd_set. */
308       FD_ZERO (&rfds);
309       FD_SET (server, &rfds);
310       max_fd = server;
311       for (client = client_list; client; client = client->next)
312         if (client->fd != -1)
313           {
314             FD_SET (client->fd, &rfds);
315             if (client->fd > max_fd)
316               max_fd = client->fd;
317           }
318
319       if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
320         continue;  /* Ignore any errors. */
321
322       if (FD_ISSET (server, &rfds)) /* New connection. */
323         {
324           struct sockaddr_un clnt_addr;
325           int fd;
326
327           addrlen = sizeof clnt_addr;
328           fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
329           if (fd == -1)
330             {
331               printf ("[accepting connection failed: %s]\n", strerror (errno));
332             }
333           else if (fd >= FD_SETSIZE)
334             {
335               close (fd);
336               printf ("[connection request denied: too many connections]\n");
337             }
338           else
339             {
340               for (client = client_list; client && client->fd != -1;
341                    client = client->next)
342                 ;
343               if (!client)
344                 {
345                   client = xcalloc (1, sizeof *client);
346                   client->next = client_list;
347                   client_list = client;
348                 }
349               client->fd = fd;
350               printf ("[client at fd %d connected]\n", client->fd);
351             }
352         }
353       for (client = client_list; client; client = client->next)
354         if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
355           {
356             char line[256];
357             int n;
358
359             n = read (client->fd, line, sizeof line - 1);
360             if (n < 0)
361               {
362                 int save_errno = errno;
363                 print_line (client, NULL); /* flush */
364                 printf ("[client at fd %d read error: %s]\n",
365                         client->fd, strerror (save_errno));
366                 close (client->fd);
367                 client->fd = -1;
368               }
369             else if (!n)
370               {
371                 print_line (client, NULL); /* flush */
372                 close (client->fd);
373                 printf ("[client at fd %d disconnected]\n", client->fd);
374                 client->fd = -1;
375               }
376             else
377               {
378                 line[n] = 0;
379                 print_line (client, line);
380               }
381           }
382     }
383
384   return 0;
385 }
386
387
388 /*
389 Local Variables:
390 compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
391 End:
392 */