e22616bfdea543d5387d6d1b08f329bfa09ae825
[obnox/wireshark/wip.git] / capture.c
1 /* capture.c
2  * Routines for packet capture windows
3  *
4  * $Id: capture.c,v 1.77 1999/10/02 06:26:45 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_LIBPCAP
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
39 #endif
40
41 #include <gtk/gtk.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <fcntl.h>
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #include <time.h>
53
54 #ifdef HAVE_SYS_SOCKET_H
55 #include <sys/socket.h>
56 #endif
57
58 #ifdef HAVE_SYS_IOCTL_H
59 #include <sys/ioctl.h>
60 #endif
61
62 #ifdef HAVE_NET_IF_H
63 #include <net/if.h>
64 #endif
65
66 #include <signal.h>
67 #include <errno.h>
68
69 #ifdef NEED_SNPRINTF_H
70 # ifdef HAVE_STDARG_H
71 #  include <stdarg.h>
72 # else
73 #  include <varargs.h>
74 # endif
75 # include "snprintf.h"
76 #endif
77
78 #include "gtk/main.h"
79 #include "packet.h"
80 #include "file.h"
81 #include "gtk/menu.h"
82 #include "capture.h"
83 #include "util.h"
84 #include "prefs.h"
85 #include "globals.h"
86
87 int sync_mode;  /* fork a child to do the capture, and sync between them */
88 int sync_pipe[2]; /* used to sync father */
89 int fork_mode;  /* fork a child to do the capture */
90 int quit_after_cap; /* Makes a "capture only mode". Implies -k */
91 gboolean capture_child; /* if this is the child for "-F"/"-S" */
92
93 static void capture_stop_cb(GtkWidget *, gpointer);
94 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
95   const u_char *);
96 static float pct(gint, gint);
97
98 typedef struct _loop_data {
99   gint           go;
100   gint           max;
101   gint           linktype;
102   gint           sync_packets;
103   packet_counts  counts;
104   wtap_dumper   *pdh;
105 } loop_data;
106
107 /* Open a specified file, or create a temporary file, and start a capture
108    to the file in question. */
109 void
110 do_capture(char *capfile_name)
111 {
112   char tmpname[128+1];
113   gboolean is_temp_file;
114   u_char c;
115   int i;
116   guint byte_count;
117   char *msg;
118   int err;
119   int capture_succeeded;
120
121   if (capfile_name != NULL) {
122     /* Try to open/create the specified file for use as a capture buffer. */
123     cf.save_file_fd = open(capfile_name, O_RDWR|O_TRUNC|O_CREAT, 0600);
124     is_temp_file = FALSE;
125   } else {
126     /* Choose a random name for the capture buffer */
127     cf.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
128     capfile_name = g_strdup(tmpname);
129     is_temp_file = TRUE;
130   }
131   if (cf.save_file_fd == -1) {
132     simple_dialog(ESD_TYPE_WARN, NULL,
133         "The file to which the capture would be saved (\"%s\")"
134         "could not be opened: %s.", tmpname, strerror(errno));
135     return;
136   }
137   close_cap_file(&cf, info_bar, file_ctx);
138   if (cf.save_file != NULL) {
139     /* If the current file is a temporary capture file, remove it. */
140     if (!cf.user_saved)
141       unlink(cf.save_file); /* silently ignore error */
142     g_free(cf.save_file);
143   }
144   cf.save_file = capfile_name;
145   cf.user_saved = !is_temp_file;
146
147   if (sync_mode || fork_mode) { /*  use fork() for capture */
148     int  fork_child;
149     char ssnap[24];
150     char scount[24];    /* need a constant for len of numbers */
151     char save_file_fd[24];
152
153     sprintf(ssnap,"%d",cf.snap); /* in lieu of itoa */
154     sprintf(scount,"%d",cf.count);
155     sprintf(save_file_fd,"%d",cf.save_file_fd);
156     signal(SIGCHLD, SIG_IGN);
157     if (sync_mode)
158       pipe(sync_pipe);
159     if ((fork_child = fork()) == 0) {
160       /* args: -i interface specification
161        * -w file to write
162        * -W file descriptor to write
163        * -c count to capture
164        * -s snaplen
165        * -S sync mode
166        * -m / -b fonts
167        * -f "filter expression"
168        */
169        if (sync_mode) {
170          close(1);
171          dup(sync_pipe[1]);
172          close(sync_pipe[0]);
173          execlp(ethereal_path, CHILD_NAME, "-i", cf.iface,
174                 "-w", cf.save_file, "-W", save_file_fd,
175                 "-c", scount, "-s", ssnap, "-S", 
176                 "-m", medium_font, "-b", bold_font,
177                 (cf.cfilter == NULL)? 0 : "-f",
178                 (cf.cfilter == NULL)? 0 : cf.cfilter,
179                 (const char *)NULL);    
180        }
181        else {
182          execlp(ethereal_path, CHILD_NAME, "-i", cf.iface,
183                 "-w", cf.save_file, "-W", save_file_fd,
184                 "-c", scount, "-s", ssnap,
185                 "-m", medium_font, "-b", bold_font,
186                 (cf.cfilter == NULL)? 0 : "-f",
187                 (cf.cfilter == NULL)? 0 : cf.cfilter,
188                 (const char *)NULL);
189        }
190     }
191     else {
192        cf.filename = cf.save_file;
193        if (sync_mode) {
194          close(sync_pipe[1]);
195
196          /* Read a byte count from "sync_pipe[0]", terminated with a
197             colon; if the count is 0, the child process created the
198             capture file and we should start reading from it, otherwise
199             the capture couldn't start and the count is a count of bytes
200             of error message, and we should display the message. */
201          byte_count = 0;
202          for (;;) {
203            i = read(sync_pipe[0], &c, 1);
204            if (i == 0) {
205              /* EOF - the child process died.
206                 XXX - reap it and report the status. */
207              simple_dialog(ESD_TYPE_WARN, NULL, "Capture child process died");
208              return;
209            }
210            if (c == ';')
211              break;
212            if (!isdigit(c)) {
213              /* Child process handed us crap. */
214              simple_dialog(ESD_TYPE_WARN, NULL,
215                 "Capture child process sent us a bad message");
216              return;
217            }
218            byte_count = byte_count*10 + c - '0';
219          }
220          if (byte_count == 0) {
221            /* Success. */
222            err = tail_cap_file(cf.save_file, &cf);
223            if (err != 0) {
224              simple_dialog(ESD_TYPE_WARN, NULL,
225                         file_open_error_message(err, FALSE), cf.save_file);
226            }
227          } else {
228            /* Failure. */
229            msg = g_malloc(byte_count + 1);
230            if (msg == NULL) {
231              simple_dialog(ESD_TYPE_WARN, NULL,
232                 "Capture child process failed, but its error message was too big.");
233            } else {
234              i = read(sync_pipe[0], msg, byte_count);
235              if (i < 0) {
236                 simple_dialog(ESD_TYPE_WARN, NULL,
237                   "Capture child process failed: Error %s reading its error message.",
238                   strerror(errno));
239              } else if (i == 0) {
240                 simple_dialog(ESD_TYPE_WARN, NULL,
241                   "Capture child process failed: EOF reading its error message.");
242              } else
243                 simple_dialog(ESD_TYPE_WARN, NULL, msg);
244              g_free(msg);
245            }
246          }
247        }
248     }
249   }
250   else {
251     capture_succeeded = capture();
252     if (quit_after_cap) {
253       /* DON'T unlink the save file.  Presumably someone wants it. */
254       gtk_exit(0);
255     }
256     if (capture_succeeded) {
257       /* Capture succeeded; read in the capture file. */
258       if ((err = open_cap_file(cf.save_file, &cf)) == 0) {
259         /* Set the read filter to NULL. */
260         cf.rfcode = NULL;
261         err = read_cap_file(&cf);
262         set_menu_sensitivity("/File/Save", TRUE);
263         set_menu_sensitivity("/File/Save As...", FALSE);
264       }
265     }
266   }
267 }
268
269 /* Do the low-level work of a capture.
270    Returns TRUE if it succeeds, FALSE otherwise. */
271 int
272 capture(void)
273 {
274   GtkWidget  *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb, *icmp_lb,
275              *ospf_lb, *gre_lb, *netbios_lb, *other_lb, *stop_bt;
276   pcap_t     *pch;
277   gchar       err_str[PCAP_ERRBUF_SIZE], label_str[32];
278   loop_data   ld;
279   bpf_u_int32 netnum, netmask;
280   time_t      upd_time, cur_time;
281   int         err, inpkts;
282   char        errmsg[1024+1];
283
284   ld.go             = TRUE;
285   ld.counts.total   = 0;
286   ld.max            = cf.count;
287   ld.linktype       = WTAP_ENCAP_UNKNOWN;
288   ld.sync_packets   = 0;
289   ld.counts.tcp     = 0;
290   ld.counts.udp     = 0;
291   ld.counts.icmp    = 0;
292   ld.counts.ospf    = 0;
293   ld.counts.gre     = 0;
294   ld.counts.netbios = 0;
295   ld.counts.other   = 0;
296   ld.pdh            = NULL;
297
298   /* Open the network interface to capture from it. */
299   pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
300
301   if (pch == NULL) {
302     /* Well, we couldn't start the capture.
303        If this is a child process that does the capturing in sync
304        mode or fork mode, it shouldn't do any UI stuff until we pop up the
305        capture-progress window, and, since we couldn't start the
306        capture, we haven't popped it up. */
307     if (!capture_child) {
308       while (gtk_events_pending()) gtk_main_iteration();
309     }
310     snprintf(errmsg, sizeof errmsg,
311       "The capture session could not be initiated (%s).\n"
312       "Please check to make sure you have sufficient permissions, and that\n"
313       "you have the proper interface specified.", err_str);
314     goto error;
315   }
316
317   if (cf.cfilter) {
318     /* A capture filter was specified; set it up. */
319     if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
320       snprintf(errmsg, sizeof errmsg,
321         "Can't use filter:  Couldn't obtain netmask info (%s).", err_str);
322       goto error;
323     }
324     if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
325       snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
326         pcap_geterr(pch));
327       goto error;
328     }
329     if (pcap_setfilter(pch, &cf.fcode) < 0) {
330       snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
331         pcap_geterr(pch));
332       goto error;
333     }
334   }
335
336   /* Set up to write to the capture file. */
337   ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(pch));
338   if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
339     strcpy(errmsg, "The network you're capturing from is of a type"
340              " that Ethereal doesn't support.");
341     goto error;
342   }
343   ld.pdh = wtap_dump_fdopen(cf.save_file_fd, WTAP_FILE_PCAP,
344                 ld.linktype, pcap_snapshot(pch), &err);
345
346   if (ld.pdh == NULL) {
347     /* We couldn't set up to write to the capture file. */
348     switch (err) {
349
350     case WTAP_ERR_CANT_OPEN:
351       strcpy(errmsg, "The file to which the capture would be saved"
352                " couldn't be created for some unknown reason.");
353       break;
354
355     case WTAP_ERR_SHORT_WRITE:
356       strcpy(errmsg, "A full header couldn't be written to the file"
357                " to which the capture would be saved.");
358       break;
359
360     default:
361       if (err < 0) {
362         sprintf(errmsg, "The file to which the capture would be"
363                      " saved (\"%s\") could not be opened: Error %d.",
364                         cf.save_file, err);
365       } else {
366         sprintf(errmsg, "The file to which the capture would be"
367                      " saved (\"%s\") could not be opened: %s.",
368                         cf.save_file, strerror(err));
369       }
370       break;
371     }
372     goto error;
373   }
374
375   if (capture_child && sync_mode) {
376     /* Well, we should be able to start capturing.
377
378        This is the child process for a sync mode capture, so sync out
379        the capture file, so the header makes it to the file system,
380        and send a "capture started successfully and capture file created"
381        message to our parent so that they'll open the capture file and
382        update its windows to indicate that we have a live capture in
383        progress. */
384     fflush(wtap_dump_file(ld.pdh));
385     write(1, "0;", 2);
386   }
387
388   cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
389   gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
390
391   /* Container for capture display widgets */
392   main_vb = gtk_vbox_new(FALSE, 1);
393   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
394   gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
395   gtk_widget_show(main_vb);
396
397   count_lb = gtk_label_new("Count: 0");
398   gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
399   gtk_widget_show(count_lb);
400
401   tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
402   gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
403   gtk_widget_show(tcp_lb);
404
405   udp_lb = gtk_label_new("UDP: 0 (0.0%)");
406   gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
407   gtk_widget_show(udp_lb);
408
409   icmp_lb = gtk_label_new("ICMP: 0 (0.0%)");
410   gtk_box_pack_start(GTK_BOX(main_vb), icmp_lb, FALSE, FALSE, 3);
411   gtk_widget_show(icmp_lb);
412
413   ospf_lb = gtk_label_new("OSPF: 0 (0.0%)");
414   gtk_box_pack_start(GTK_BOX(main_vb), ospf_lb, FALSE, FALSE, 3);
415   gtk_widget_show(ospf_lb);
416
417   gre_lb = gtk_label_new("GRE: 0 (0.0%)");
418   gtk_box_pack_start(GTK_BOX(main_vb), gre_lb, FALSE, FALSE, 3);
419   gtk_widget_show(gre_lb);
420
421   netbios_lb = gtk_label_new("NetBIOS: 0 (0.0%)");
422   gtk_box_pack_start(GTK_BOX(main_vb), netbios_lb, FALSE, FALSE, 3);
423   gtk_widget_show(netbios_lb);
424
425   other_lb = gtk_label_new("Other: 0 (0.0%)");
426   gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
427   gtk_widget_show(other_lb);
428
429   stop_bt = gtk_button_new_with_label ("Stop");
430   gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
431     GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
432   gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
433   GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
434   gtk_widget_grab_default(stop_bt);
435   GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
436   gtk_widget_grab_default(stop_bt);
437   gtk_widget_show(stop_bt);
438
439   gtk_widget_show(cap_w);
440   gtk_grab_add(cap_w);
441
442   upd_time = time(NULL);
443   while (ld.go) {
444     while (gtk_events_pending()) gtk_main_iteration();
445     inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
446     if (inpkts > 0)
447       ld.sync_packets += inpkts;
448     /* Only update once a second so as not to overload slow displays */
449     cur_time = time(NULL);
450     if (cur_time > upd_time) {
451       upd_time = cur_time;
452
453       sprintf(label_str, "Count: %d", ld.counts.total);
454       gtk_label_set(GTK_LABEL(count_lb), label_str);
455
456       sprintf(label_str, "TCP: %d (%.1f%%)", ld.counts.tcp,
457                 pct(ld.counts.tcp, ld.counts.total));
458       gtk_label_set(GTK_LABEL(tcp_lb), label_str);
459
460       sprintf(label_str, "UDP: %d (%.1f%%)", ld.counts.udp,
461                 pct(ld.counts.udp, ld.counts.total));
462       gtk_label_set(GTK_LABEL(udp_lb), label_str);
463
464       sprintf(label_str, "ICMP: %d (%.1f%%)", ld.counts.icmp,
465                 pct(ld.counts.icmp, ld.counts.total));
466       gtk_label_set(GTK_LABEL(icmp_lb), label_str);
467
468       sprintf(label_str, "OSPF: %d (%.1f%%)", ld.counts.ospf,
469                 pct(ld.counts.ospf, ld.counts.total));
470       gtk_label_set(GTK_LABEL(ospf_lb), label_str);
471
472       sprintf(label_str, "GRE: %d (%.1f%%)", ld.counts.gre,
473                 pct(ld.counts.gre, ld.counts.total));
474       gtk_label_set(GTK_LABEL(gre_lb), label_str);
475
476       sprintf(label_str, "NetBIOS: %d (%.1f%%)", ld.counts.netbios,
477                 pct(ld.counts.netbios, ld.counts.total));
478       gtk_label_set(GTK_LABEL(netbios_lb), label_str);
479
480       sprintf(label_str, "Other: %d (%.1f%%)", ld.counts.other,
481                 pct(ld.counts.other, ld.counts.total));
482       gtk_label_set(GTK_LABEL(other_lb), label_str);
483
484       /* do sync here, too */
485       fflush(wtap_dump_file(ld.pdh));
486       if (capture_child && sync_mode && ld.sync_packets) {
487         /* This is the child process for a sync mode capture, so send
488            our parent a message saying we've written out "ld.sync_packets"
489            packets to the capture file. */
490         char tmp[20];
491         sprintf(tmp, "%d*", ld.sync_packets);
492         write(1, tmp, strlen(tmp));
493         ld.sync_packets = 0;
494       }
495     }
496   }
497     
498   if (!wtap_dump_close(ld.pdh, &err)) {
499     /* XXX - in fork mode, this may not pop up, or, if it does,
500        it may disappear as soon as we exit.
501
502        We should have the parent process, while it's reading
503        the packet count update messages, catch error messages
504        and pop up a message box if it sees one. */
505     switch (err) {
506
507     case WTAP_ERR_CANT_CLOSE:
508       simple_dialog(ESD_TYPE_WARN, NULL,
509                 "The file to which the capture was being saved"
510                 " couldn't be closed for some unknown reason.");
511       break;
512
513     case WTAP_ERR_SHORT_WRITE:
514       simple_dialog(ESD_TYPE_WARN, NULL,
515                 "Not all the data could be written to the file"
516                 " to which the capture was being saved.");
517       break;
518
519     default:
520       if (err < 0) {
521         simple_dialog(ESD_TYPE_WARN, NULL,
522                 "The file to which the capture was being"
523                 " saved (\"%s\") could not be closed: Error %d.",
524                 cf.save_file, err);
525       } else {
526         simple_dialog(ESD_TYPE_WARN, NULL,
527                 "The file to which the capture was being"
528                 " saved (\"%s\") could not be closed: %s.",
529                 cf.save_file, strerror(err));
530       }
531       break;
532     }
533   }
534   pcap_close(pch);
535
536   gtk_grab_remove(GTK_WIDGET(cap_w));
537   gtk_widget_destroy(GTK_WIDGET(cap_w));
538
539   return TRUE;
540
541 error:
542   /* We couldn't even start the capture, so get rid of the capture
543      file. */
544   unlink(cf.save_file); /* silently ignore error */
545   if (capture_child && sync_mode) {
546     /* This is the child process for a sync mode capture.
547        Send the error message to our parent, so they can display a
548        dialog box containing it. */
549     int msglen = strlen(errmsg);
550     char lenbuf[10+1+1];
551     sprintf(lenbuf, "%u;", msglen);
552     write(1, lenbuf, strlen(lenbuf));
553     write(1, errmsg, msglen);
554   } else {
555     /* Display the dialog box ourselves; there's no parent. */
556     simple_dialog(ESD_TYPE_WARN, NULL, errmsg);
557   }
558   if (pch != NULL)
559     pcap_close(pch);
560
561   return FALSE;
562 }
563
564 static float
565 pct(gint num, gint denom) {
566   if (denom) {
567     return (float) num * 100.0 / (float) denom;
568   } else {
569     return 0.0;
570   }
571 }
572
573 static void
574 capture_stop_cb(GtkWidget *w, gpointer data) {
575   loop_data *ld = (loop_data *) data;
576   
577   ld->go = FALSE;
578 }
579
580 static void
581 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
582   const u_char *pd) {
583   struct wtap_pkthdr whdr;
584   loop_data *ld = (loop_data *) user;
585   int err;
586
587   if ((++ld->counts.total >= ld->max) && (ld->max > 0)) 
588   {
589      ld->go = FALSE;
590   }
591   if (ld->pdh) {
592      whdr.ts = phdr->ts;
593      whdr.caplen = phdr->caplen;
594      whdr.len = phdr->len;
595      whdr.pkt_encap = ld->linktype;
596
597      /* XXX - do something if this fails */
598      wtap_dump(ld->pdh, &whdr, pd, &err);
599   }
600     
601   switch (ld->linktype) {
602     case WTAP_ENCAP_ETHERNET:
603       capture_eth(pd, phdr->caplen, &ld->counts);
604       break;
605     case WTAP_ENCAP_FDDI:
606     case WTAP_ENCAP_FDDI_BITSWAPPED:
607       capture_fddi(pd, phdr->caplen, &ld->counts);
608       break;
609     case WTAP_ENCAP_TR:
610       capture_tr(pd, phdr->caplen, &ld->counts);
611       break;
612     case WTAP_ENCAP_NULL:
613       capture_null(pd, phdr->caplen, &ld->counts);
614       break;
615     case WTAP_ENCAP_PPP:
616       capture_ppp(pd, phdr->caplen, &ld->counts);
617       break;
618     case WTAP_ENCAP_RAW_IP:
619       capture_raw(pd, phdr->caplen, &ld->counts);
620       break;
621     /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
622        with LLC header following; we should implement it at some
623        point. */
624   }
625 }
626
627 #endif /* HAVE_LIBPCAP */