Various code cleanup in the capture pipe stuff
[obnox/wireshark/wip.git] / dumpcap.c
1 /* dumpcap.c
2  *
3  * $Id$
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@ethereal.com>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <glib.h>
29
30 #include <string.h>
31 #include <ctype.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #ifdef NEED_GETOPT_H
38 #include "getopt.h"
39 #endif
40
41 #ifdef HAVE_NETDB_H
42 #include <netdb.h>
43 #endif
44
45 #include "ringbuffer.h"
46 #include "clopts_common.h"
47 #include "cmdarg_err.h"
48 #include "version_info.h"
49
50 #include <pcap.h>
51 #include "capture-pcap-util.h"
52
53 #ifdef _WIN32
54 #include "capture-wpcap.h"
55 #endif
56
57 #include "capture.h"
58 #include "capture_loop.h"
59 #include "capture_sync.h"
60
61 #include "simple_dialog.h"
62 #include "util.h"
63 #include "log.h"
64 #include "file_util.h"
65
66
67 /*#define DEBUG_DUMPCAP*/
68
69 gboolean capture_child = FALSE; /* FALSE: standalone call, TRUE: this is an Ethereal capture child */
70
71 static void
72 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
73                     const char *message, gpointer user_data _U_);
74
75 /* capture related options */
76 capture_options global_capture_opts;
77 capture_options *capture_opts = &global_capture_opts;
78
79 #if __GNUC__ >= 2
80 void exit_main(int err) __attribute__ ((noreturn));
81 #else
82 void exit_main(int err);
83 #endif
84
85
86 static void
87 print_usage(gboolean print_ver) {
88
89   FILE *output;
90
91
92   if (print_ver) {
93     output = stdout;
94     fprintf(output,
95         "Dumpcap " VERSION "%s\n"
96         "Capture network packets and dump them into a libpcap file.\n"
97         "See http://www.ethereal.com for more information.\n",
98         svnversion);
99   } else {
100     output = stderr;
101   }
102   fprintf(output, "\nUsage: dumpcap [options] ...\n");
103   fprintf(output, "\n");
104   fprintf(output, "Capture interface:\n");
105   fprintf(output, "  -i <interface>           name or idx of interface (def: first none loopback)\n");
106   fprintf(output, "  -f <capture filter>      packet filter in libpcap filter syntax\n");
107   fprintf(output, "  -s <snaplen>             packet snapshot length (def: 65535)\n");
108   fprintf(output, "  -p                       don't capture in promiscuous mode\n");
109 #ifdef _WIN32
110   fprintf(output, "  -B <buffer size>         size of kernel buffer (def: 1MB)\n");
111 #endif
112   fprintf(output, "  -y <link type>           link layer type (def: first appropriate)\n");
113   fprintf(output, "  -D                       print list of interfaces and exit\n");
114   fprintf(output, "  -L                       print list of link-layer types of iface and exit\n");
115   fprintf(output, "\n");
116   fprintf(output, "Stop conditions:\n");
117   fprintf(output, "  -c <packet count>        stop after n packets (def: infinite)\n");
118   fprintf(output, "  -a <autostop cond.> ...  duration:NUM - stop after NUM seconds\n");
119   fprintf(output, "                           filesize:NUM - stop this file after NUM KB\n");
120   fprintf(output, "                              files:NUM - stop after NUM files\n");
121   /*fprintf(output, "\n");*/
122   fprintf(output, "Output (files):\n");
123   fprintf(output, "  -w <filename>            name of file to save (def: tempfile)\n");
124   fprintf(output, "  -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
125   fprintf(output, "                           filesize:NUM - switch to next file after NUM KB\n");
126   fprintf(output, "                              files:NUM - ringbuffer: replace after NUM files\n");
127   /*fprintf(output, "\n");*/
128   fprintf(output, "Miscellaneous:\n");
129   fprintf(output, "  -v                       print version information and exit\n");
130   fprintf(output, "  -h                       display this help and exit\n");
131   fprintf(output, "\n");
132   fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcap\n");
133   fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcap\"\n");
134   fprintf(output, "\n");
135   fprintf(output, "Use Ctrl-C to stop capturing at any time.\n");
136 }
137
138 static void
139 show_version(GString *comp_info_str, GString *runtime_info_str)
140 {
141
142   printf(
143         "Dumpcap " VERSION "%s\n"
144         "\n"
145         "%s\n"
146         "%s\n"
147         "%s\n"
148         "See http://www.ethereal.com for more information.\n",
149         svnversion, get_copyright_info() ,comp_info_str->str, runtime_info_str->str);
150 }
151
152 /*
153  * Report an error in command-line arguments.
154  */
155 void
156 cmdarg_err(const char *fmt, ...)
157 {
158   va_list ap;
159
160   if(capture_child) {
161     /* XXX - convert to g_log */
162   } else {
163     va_start(ap, fmt);
164     fprintf(stderr, "dumpcap: ");
165     vfprintf(stderr, fmt, ap);
166     fprintf(stderr, "\n");
167     va_end(ap);
168   }
169 }
170
171 /*
172  * Report additional information for an error in command-line arguments.
173  */
174 void
175 cmdarg_err_cont(const char *fmt, ...)
176 {
177   va_list ap;
178
179   if(capture_child) {
180     /* XXX - convert to g_log */
181   } else {
182     va_start(ap, fmt);
183     vfprintf(stderr, fmt, ap);
184     fprintf(stderr, "\n");
185     va_end(ap);
186   }
187 }
188
189
190 #ifdef _WIN32
191 BOOL WINAPI ConsoleCtrlHandlerRoutine(DWORD dwCtrlType)
192 {
193     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
194         "Console: Ctrl+C");
195     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
196         "Console: Ctrl+C CtrlType: %u", dwCtrlType);
197
198     capture_loop_stop();
199
200     return TRUE;
201 }
202 #endif
203
204 void exit_main(int status)
205 {
206 #ifdef _WIN32
207   /* Shutdown windows sockets */
208   WSACleanup();
209 #endif
210
211   /* can be helpful for debugging */
212 #ifdef DEBUG_DUMPCAP
213   printf("Press any key\n");
214   _getch();
215 #endif
216
217   exit(status);
218 }
219
220
221 /* And now our feature presentation... [ fade to music ] */
222 int
223 main(int argc, char *argv[])
224 {
225   int                  opt;
226   extern char         *optarg;
227   gboolean             arg_error = FALSE;
228   GString             *comp_info_str;
229   GString             *runtime_info_str;
230
231 #ifdef _WIN32
232   WSADATA              wsaData;
233 #endif  /* _WIN32 */
234
235   gboolean             start_capture = TRUE;
236   gboolean             stats_known;
237   struct pcap_stat     stats;
238   GLogLevelFlags       log_flags;
239   gboolean             list_link_layer_types = FALSE;
240   int                  status;
241
242 #define OPTSTRING_INIT "a:b:c:Df:hi:Lps:vw:y:Z"
243
244 #ifdef _WIN32
245 #define OPTSTRING_WIN32 "B:"
246 #else
247 #define OPTSTRING_WIN32 ""
248 #endif  /* _WIN32 */
249
250   char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] =
251     OPTSTRING_INIT OPTSTRING_WIN32;
252
253 #ifdef _WIN32
254   /* Load wpcap if possible. Do this before collecting the run-time version information */
255   load_wpcap();
256
257   /* ... and also load the packet.dll from wpcap */
258   /* XXX - currently not required, may change later. */
259   /*wpcap_packet_load();*/
260
261   /* Start windows sockets */
262   WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
263
264   /* Set handler for Ctrl+C key */
265   SetConsoleCtrlHandler(&ConsoleCtrlHandlerRoutine, TRUE);
266 #endif  /* _WIN32 */
267
268   /* Assemble the compile-time version information string */
269   comp_info_str = g_string_new("Compiled ");
270   g_string_append(comp_info_str, "with ");
271   get_compiled_version_info(comp_info_str);
272
273   /* Assemble the run-time version information string */
274   runtime_info_str = g_string_new("Running ");
275   get_runtime_version_info(runtime_info_str);
276
277   /* the default_log_handler will use stdout, which makes trouble in */
278   /* capture child mode, as it uses stdout for it's sync_pipe */
279   /* so do the filtering in the console_log_handler and not here */
280   log_flags = 
281                     G_LOG_LEVEL_ERROR|
282                     G_LOG_LEVEL_CRITICAL|
283                     G_LOG_LEVEL_WARNING|
284                     G_LOG_LEVEL_MESSAGE|
285                     G_LOG_LEVEL_INFO|
286                     G_LOG_LEVEL_DEBUG|
287                     G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION;
288
289   g_log_set_handler(NULL,
290                     log_flags,
291                     console_log_handler, NULL /* user_data */);
292   g_log_set_handler(LOG_DOMAIN_MAIN,
293                     log_flags,
294                     console_log_handler, NULL /* user_data */);
295   g_log_set_handler(LOG_DOMAIN_CAPTURE,
296                     log_flags,
297             console_log_handler, NULL /* user_data */);
298   g_log_set_handler(LOG_DOMAIN_CAPTURE_CHILD,
299                     log_flags,
300             console_log_handler, NULL /* user_data */);
301
302   /* Set the initial values in the capture_opts. This might be overwritten 
303      by the command line parameters. */
304   capture_opts_init(capture_opts, NULL);
305
306   capture_opts->snaplen             = MIN_PACKET_SIZE;
307   capture_opts->has_ring_num_files  = TRUE;
308
309   /* Now get our args */
310   while ((opt = getopt(argc, argv, optstring)) != -1) {
311     switch (opt) {
312       case 'h':        /* Print help and exit */
313         print_usage(TRUE);
314         exit_main(0);
315         break;
316       case 'v':        /* Show version and exit */
317         show_version(comp_info_str, runtime_info_str);
318         exit_main(0);
319         break;
320       /*** capture option specific ***/
321       case 'a':        /* autostop criteria */
322       case 'b':        /* Ringbuffer option */
323       case 'c':        /* Capture x packets */
324       case 'f':        /* capture filter */
325       case 'i':        /* Use interface x */
326       case 'p':        /* Don't capture in promiscuous mode */
327       case 's':        /* Set the snapshot (capture) length */
328       case 'w':        /* Write to capture file x */
329       case 'y':        /* Set the pcap data link type */
330 #ifdef _WIN32
331       case 'B':        /* Buffer size */
332 #endif /* _WIN32 */
333         status = capture_opts_add_opt(capture_opts, opt, optarg, &start_capture);
334         if(status != 0) {
335             exit_main(status);
336         }
337         break;
338       /*** hidden option: Ethereal child mode (using binary output messages) ***/
339       case 'Z':
340           capture_child = TRUE;
341 #ifdef _WIN32
342           /* set output pipe to binary mode, to avoid ugly text conversions */
343                   _setmode(1, O_BINARY);
344 #endif
345           break;
346
347       /*** all non capture option specific ***/
348       case 'D':        /* Print a list of capture devices and exit */
349         status = capture_opts_list_interfaces();
350         exit_main(status);
351         break;
352       case 'L':        /* Print list of link-layer types and exit */
353         list_link_layer_types = TRUE;
354         break;
355       default:
356       case '?':        /* Bad flag - print usage message */
357         cmdarg_err("Invalid Option: %s", argv[optind-1]);
358         arg_error = TRUE;
359         break;
360     }
361   }
362   argc -= optind;
363   argv += optind;
364   if (argc >= 1) {
365       /* user specified file name as regular command-line argument */
366       /* XXX - use it as the capture file name (or something else)? */
367     argc--;
368     argv++;
369   }
370
371   if (argc != 0) {
372     /*
373      * Extra command line arguments were specified; complain.
374      */
375     cmdarg_err("Invalid argument: %s", argv[0]);
376     arg_error = TRUE;
377   }
378
379   if (arg_error) {
380     print_usage(FALSE);
381     exit_main(1);
382   }
383
384   if (list_link_layer_types) {
385     /* We're supposed to list the link-layer types for an interface;
386        did the user also specify a capture file to be read? */
387     /* No - did they specify a ring buffer option? */
388     if (capture_opts->multi_files_on) {
389       cmdarg_err("Ring buffer requested, but a capture isn't being done.");
390       exit_main(1);
391     }
392   } else {
393     /* No - was the ring buffer option specified and, if so, does it make
394        sense? */
395     if (capture_opts->multi_files_on) {
396       /* Ring buffer works only under certain conditions:
397          a) ring buffer does not work with temporary files;
398          b) it makes no sense to enable the ring buffer if the maximum
399             file size is set to "infinite". */
400       if (capture_opts->save_file == NULL) {
401         cmdarg_err("Ring buffer requested, but capture isn't being saved to a permanent file.");
402         capture_opts->multi_files_on = FALSE;
403       }
404       if (!capture_opts->has_autostop_filesize && !capture_opts->has_file_duration) {
405         cmdarg_err("Ring buffer requested, but no maximum capture file size or duration were specified.");
406 /* XXX - this must be redesigned as the conditions changed */
407 /*      capture_opts->multi_files_on = FALSE;*/
408       }
409     }
410   }
411
412   if (capture_opts_trim_iface(capture_opts, NULL) == FALSE) {
413         cmdarg_err("No capture interfaces available (maybe lack of privileges?).");
414     exit_main(1);
415   }
416
417   /* Let the user know what interface was chosen. */
418   /* get_interface_descriptive_name() is not available! */
419   g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_MESSAGE, "Interface: %s\n", capture_opts->iface);
420
421   if (list_link_layer_types) {
422     status = capture_opts_list_link_layer_types(capture_opts);
423     exit_main(status);
424   }
425
426   capture_opts_trim_snaplen(capture_opts, MIN_PACKET_SIZE);
427   capture_opts_trim_ring_num_files(capture_opts);
428
429   /* Now start the capture. */
430
431   if(capture_loop_start(capture_opts, &stats_known, &stats) == TRUE) {
432       /* capture ok */
433       exit_main(0);
434   } else {
435       /* capture failed */
436       exit_main(1);
437   }
438 }
439
440
441 static void
442 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
443                     const char *message, gpointer user_data _U_)
444 {
445   time_t curr;
446   struct tm *today;
447   const char *level;
448
449
450   if(capture_child) {
451     return;
452   }
453
454   /* ignore log message, if log_level isn't interesting */
455   if( !(log_level & G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_DEBUG|G_LOG_LEVEL_INFO))) {
456 #ifndef DEBUG_DUMPCAP
457     return;
458 #endif
459   }
460
461   /* create a "timestamp" */
462   time(&curr);
463   today = localtime(&curr);    
464
465     switch(log_level & G_LOG_LEVEL_MASK) {
466     case G_LOG_LEVEL_ERROR:
467         level = "Err ";
468         break;
469     case G_LOG_LEVEL_CRITICAL:
470         level = "Crit";
471         break;
472     case G_LOG_LEVEL_WARNING:
473         level = "Warn";
474         break;
475     case G_LOG_LEVEL_MESSAGE:
476         level = "Msg ";
477         break;
478     case G_LOG_LEVEL_INFO:
479         level = "Info";
480         break;
481     case G_LOG_LEVEL_DEBUG:
482         level = "Dbg ";
483         break;
484     default:
485         fprintf(stderr, "unknown log_level %u\n", log_level);
486         level = NULL;
487         g_assert_not_reached();
488     }
489
490     /* don't use printf (stdout), in child mode we're using stdout for the sync_pipe */
491     if(log_level & G_LOG_LEVEL_MESSAGE) {
492         /* normal user messages without additional infos */
493         fprintf(stderr, "%s\n", message);
494         fflush(stderr);
495     } else {
496         /* info/debug messages with additional infos */
497         fprintf(stderr, "%02u:%02u:%02u %8s %s %s\n",
498                 today->tm_hour, today->tm_min, today->tm_sec,
499                 log_domain != NULL ? log_domain : "",
500                 level, message);
501         fflush(stderr);
502     }
503 }
504
505
506 /****************************************************************************************************************/
507 /* sync_pipe handling */
508
509
510 /* write a single message header to the recipient pipe */
511 static int
512 pipe_write_header(int pipe, char indicator, int length)
513 {
514     guchar header[1+3]; /* indicator + 3-byte len */
515
516
517     g_assert(length <= SP_MAX_MSG_LEN);
518
519     /* write header (indicator + 3-byte len) */
520     header[0] = indicator;
521     header[1] = (length >> 16) & 0xFF;
522     header[2] = (length >> 8) & 0xFF;
523     header[3] = (length >> 0) & 0xFF;
524
525     /* write header */
526     return write(pipe, header, sizeof header);
527 }
528
529
530 /* write a message to the recipient pipe in the standard format 
531    (3 digit message length (excluding length and indicator field), 
532    1 byte message indicator and the rest is the message).
533    If msg is NULL, the message has only a length and indicator. */
534 static void
535 pipe_write_block(int pipe, char indicator, const char *msg)
536 {
537     int ret;
538     size_t len;
539
540     /*g_warning("write %d enter", pipe);*/
541
542     if(msg != NULL) {
543         len = strlen(msg) + 1;    /* including the terminating '\0'! */
544     } else {
545         len = 0;
546     }
547
548     /* write header (indicator + 3-byte len) */
549     ret = pipe_write_header(pipe, indicator, len);
550     if(ret == -1) {
551         return;
552     }
553
554     /* write value (if we have one) */
555     if(len) {
556         /*g_warning("write %d indicator: %c value len: %u msg: %s", pipe, indicator, len, msg);*/
557         ret = write(pipe, msg, len);
558         if(ret == -1) {
559             return;
560         }
561     } else {
562         /*g_warning("write %d indicator: %c no value", pipe, indicator);*/
563     }
564
565     /*g_warning("write %d leave", pipe);*/
566 }
567
568
569 void
570 sync_pipe_packet_count_to_parent(int packet_count)
571 {
572     char tmp[SP_DECISIZE+1+1];
573     static int count = 0;
574
575
576     if(capture_child) {
577         g_snprintf(tmp, sizeof(tmp), "%d", packet_count);
578         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets: %s", tmp);
579         pipe_write_block(1, SP_PACKET_COUNT, tmp);
580     } else {
581         count += packet_count;
582         fprintf(stderr, "\rPackets: %u ", count);
583         /* stderr could be line buffered */
584         fflush(stderr);
585     }
586 }
587
588 void
589 sync_pipe_filename_to_parent(const char *filename)
590 {
591
592     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_MESSAGE, "File: %s", filename);
593
594     if(capture_child) {
595         pipe_write_block(1, SP_FILE, filename);
596     }
597 }
598
599 void
600 sync_pipe_cfilter_error_to_parent(const char *cfilter _U_, const char *errmsg)
601 {
602
603     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Capture filter error: %s", errmsg);
604
605     if (capture_child) {
606         pipe_write_block(1, SP_BAD_FILTER, errmsg);
607     }
608 }
609
610 void
611 sync_pipe_errmsg_to_parent(const char *error_msg, const char *secondary_error_msg)
612 {
613
614     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_MESSAGE, 
615         "Primary Error: %s", error_msg);
616     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_MESSAGE, 
617         "Secondary Error: %s", secondary_error_msg);
618
619     if(capture_child) {
620         /* first write a "master header" with the length of the two messages plus their "slave headers" */
621         pipe_write_header(1, SP_ERROR_MSG, strlen(error_msg) + 1 + 4 + strlen(secondary_error_msg) + 1 + 4);
622         pipe_write_block(1, SP_ERROR_MSG, error_msg);
623         pipe_write_block(1, SP_ERROR_MSG, secondary_error_msg);
624     }
625 }
626
627 void
628 sync_pipe_drops_to_parent(int drops)
629 {
630     char tmp[SP_DECISIZE+1+1];
631
632
633     g_snprintf(tmp, sizeof(tmp), "%d", drops);
634     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_MESSAGE, "Packets dropped: %s", tmp);
635
636     if(capture_child) {
637         pipe_write_block(1, SP_DROPS, tmp);
638     }
639 }
640
641
642 /****************************************************************************************************************/
643 /* signal_pipe handling */
644
645
646 #ifdef _WIN32
647 gboolean
648 signal_pipe_check_running(void)
649 {
650     /* any news from our parent (stdin)? -> just stop the capture */
651     HANDLE handle;
652     DWORD avail = 0;
653     gboolean result;
654
655
656     /* if we are running standalone, no check required */
657     if(!capture_child) {
658         return TRUE;
659     }
660
661     handle = (HANDLE) GetStdHandle(STD_INPUT_HANDLE);
662     result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
663
664     if(!result || avail > 0) {
665         /* peek failed or some bytes really available */
666         /* (if not piping from stdin this would fail) */
667         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
668             "Signal pipe: Stop capture");
669         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
670             "Signal pipe: handle: %x result: %u avail: %u", handle, result, avail);
671         return FALSE;
672     } else {
673         /* pipe ok and no bytes available */
674         return TRUE;
675     }
676 }
677 #endif
678
679
680 /****************************************************************************************************************/
681 /* Stub functions */
682
683
684 const char *netsnmp_get_version(void) { return ""; }