1 /*
2  * TCPVIEW
3  *
4  * Author:	Martin Hunt
5  *		Networks and Distributed Computing
6  *		Computing & Communications
7  *		University of Washington
8  *		Administration Building, AG-44
9  *		Seattle, WA  98195
10  *		Internet: martinh@cac.washington.edu
11  *
12  *
13  * Copyright 1992 by the University of Washington
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted, provided
17  * that the above copyright notice appears in all copies and that both the
18  * above copyright notice and this permission notice appear in supporting
19  * documentation, and that the name of the University of Washington not be
20  * used in advertising or publicity pertaining to distribution of the software
21  * without specific, written prior permission.  This software is made
22  * available "as is", and
23  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
24  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
26  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
27  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
28  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
29  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
30  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
31  *
32  */
33 
34 #ifndef lint
35 static char rcsid[] =
36     "@(#) $Header: /usr/staff/martinh/tcpview/RCS/capture.c,v 1.2 1993/04/22 20:14:24 martinh Exp $ (UW)";
37 #endif
38 
39 #include <stdio.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/wait.h>
43 #include <signal.h>
44 
45 #include  <X11/IntrinsicP.h>
46 #include  <X11/StringDefs.h>
47 #include  <X11/CoreP.h>
48 #include  <Xm/Xm.h>
49 #include  <Xm/Frame.h>
50 #include  <Xm/MessageB.h>
51 #include  <Xm/RowColumn.h>
52 #include  <Xm/Separator.h>
53 #include  <Xm/ToggleB.h>
54 #include  <Xm/BulletinB.h>
55 #include  <Xm/SelectioB.h>
56 #include  <Xm/TextF.h>
57 #include  <Xm/Form.h>
58 
59 #include "net/bpf.h"
60 #include "interface.h"
61 #include "tcpview.h"
62 #include "motif.h"
63 #include "filter.h"
64 
65 #ifdef __STDC__
66 struct bpf_program *parse(char *, int, int, u_long);  /* gencode.c */
67 void readfile(char *);
68 static void max_bytes_callback( Widget, caddr_t, caddr_t );
69 static void time_limit_callback( Widget, caddr_t, caddr_t );
70 static void num_frame_callback( Widget, caddr_t, caddr_t );
71 static void device_callback( Widget, caddr_t, caddr_t );
72 static void prom_callback( Widget, caddr_t, caddr_t );
73 static void done_callback( Widget, caddr_t, caddr_t );
74 static void cancel_callback( Widget, caddr_t, caddr_t );
75 static void dev_cancel( Widget, caddr_t, caddr_t );
76 #else
77 struct bpf_program *parse();  /* gencode.c */
78 void readfile();
79 static void max_bytes_callback();
80 static void time_limit_callback();
81 static void num_frame_callback();
82 static void device_callback();
83 static void prom_callback();
84 static void done_callback();
85 static void cancel_callback();
86 static void dev_cancel();
87 #endif /* __STDC__ */
88 
89 static   int if_fd;
90 static   char *strbuf;
91 static Widget Work, bb_widget, mb_widget, tl_widget, nf_widget, dev_widget, pr_widget;
92 static char *TempName;
93 static XtIntervalId TimeoutID = 0;
94 static char StderrName[64];
95 
96 /* global configuration variables */
97 int MaxBytes;
98 char *Device=0;
99 
100 static long TimeLimit = 0;
101 static long NumberOfFrames = -1;
102 static char *promstring[] = { "ON", "OFF" } ;
103 static u_short mb_semaphore;
104 static u_short tl_semaphore;
105 static u_short nf_semaphore;
106 static u_short dev_semaphore;
107 
ltos(num)108 char *ltos( num )
109 long num;
110 {
111   static char str[32];
112   sprintf(str,"%d",num);
113   return( str );
114 }
115 
cap_opt_callback(widget,name,call_data)116 void cap_opt_callback( widget, name, call_data )
117 Widget  widget;
118 char    *name;
119 caddr_t call_data;
120 {
121   Widget ok, main_row, frame, rc, rd;
122   int n;
123   Arg args[5];
124 
125   mb_semaphore = tl_semaphore = nf_semaphore = dev_semaphore = 0;
126 
127   n=0;
128   XtSetArg( args[n], XmNautoUnmanage, False ); n++;
129   XtSetArg( args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL ); n++;
130   bb_widget = XmCreateBulletinBoardDialog (widget,"capture_options",args,n);
131 
132   n = 0;
133   XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
134   main_row = XmCreateRowColumn( bb_widget,"main",args,n);
135   XtManageChild( main_row );
136 
137   n=0;
138   XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
139   frame = XmCreateFrame (main_row,"frame",args,n);
140   XtManageChild(frame);
141 
142   n = 0;
143   XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
144   rc = XmCreateRowColumn( frame,"row",args,n);
145   XtManageChild( rc );
146 
147   n=0;
148   CreateLabelWidget( rc, "interface_opts","  INTERFACE OPTIONS  ", args, n );
149   XmCreateSeparator( rc, "sep", args, n );
150 
151   n=0;
152   XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
153   rd = XmCreateRowColumn( rc,"data",args,n);
154   XtManageChild( rd );
155   CreateLabelWidget( rd, "device", "Device Name ", args, 0 );
156   if( Device == NULL ) {
157     Device = lookup_device();
158     if( Device == NULL )
159       eprint("Can't find any interfaces");
160   }
161   dev_widget = CreateSimpleButton( rd, Device, device_callback, (caddr_t)0 );
162 
163   n=0;
164   XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
165   rd = XmCreateRowColumn( rc,"rd",args,n);
166   XtManageChild( rd );
167   CreateLabelWidget( rd, "prom_mode", "Promiscuous Mode ", args, 0 );
168   pr_widget = CreateSimpleButton( rd, promstring[pflag], prom_callback, NULL );
169 
170   n=0;
171   XtSetArg ( args[n], XmNshadowThickness, 4 ); n++;
172   frame = XmCreateFrame (main_row,"frame",args,n);
173   XtManageChild(frame);
174 
175   n = 0;
176   XtSetArg( args[n], XmNorientation, XmVERTICAL ); n++;
177   rc = XmCreateRowColumn( frame,"row2",args,n);
178   XtManageChild( rc );
179 
180   n=0;
181   CreateLabelWidget( rc, "cap_opts","  CAPTURE OPTIONS  ", args, n );
182   XmCreateSeparator( rc, "sep", args, n );
183 
184   n=0;
185   XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
186   rd = XmCreateRowColumn( rc,"data",args,n);
187   XtManageChild( rd );
188   CreateLabelWidget( rd, "frame_num", "Number of Frames ", args, 0 );
189   if( NumberOfFrames > 0 )
190     nf_widget = CreateSimpleButton( rd, ltos(NumberOfFrames), num_frame_callback, (caddr_t)0 );
191   else
192     nf_widget = CreateSimpleButton( rd, "infinite", num_frame_callback, (caddr_t)0 );
193 
194   n=0;
195   XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
196   rd = XmCreateRowColumn( rc,"data",args,n);
197   XtManageChild( rd );
198   CreateLabelWidget( rd, "time", "Time Limit ", args, n );
199   if( TimeLimit > 0 )
200     tl_widget = CreateSimpleButton( rd, ltos(TimeLimit), time_limit_callback, (caddr_t)0 );
201   else
202     tl_widget = CreateSimpleButton( rd, "infinite", time_limit_callback, (caddr_t)0 );
203   CreateLabelWidget( rd, "time_sec", " seconds", args, 0 );
204 
205   n=0;
206   XtSetArg( args[n], XmNorientation, XmHORIZONTAL ); n++;
207   rd = XmCreateRowColumn( rc,"data",args,n);
208   XtManageChild( rd );
209   CreateLabelWidget( rd, "bytes", "Max Bytes Per Frame ", args, 0 );
210   mb_widget = CreateSimpleButton( rd, ltos(MaxBytes), max_bytes_callback, (caddr_t)0 );
211 
212   /* create 'OK' pushbutton */
213   n = 0;
214   ok = CreatePushButton( main_row, "done", " DONE ", args, n, done_callback);
215 
216   /* Set OK button as default */
217   BulletinDefaultButton( bb_widget, ok );
218 
219   XtManageChild( bb_widget );
220 }       /* cap_opt_callback */
221 
get_long(parent,def,name,title,callback,semaphore)222 void get_long( parent, def, name, title, callback, semaphore )
223 Widget parent;
224 long def;
225 char *name, *title;
226 void (*callback)();
227 u_short *semaphore;
228 {
229   Widget w;
230   Arg args[2];
231   int n;
232   XmString ms, ms2;
233 
234   ms = XmStringCreateSimple(title);
235   ms2 = XmStringCreateSimple( ltos(def) );
236   n = 0;
237   XtSetArg (args[n], XmNselectionLabelString, ms); n++;
238   if( def > 0 ) {
239     XtSetArg (args[n], XmNtextString, ms2); n++;
240   }
241   w = XmCreatePromptDialog( bb_widget, name ,args,n);
242   XtAddCallback( w, XmNokCallback, (*callback), (caddr_t)0);
243   XtAddCallback( w, XmNcancelCallback, cancel_callback, semaphore);
244   XtAddCallback( w, XmNunmapCallback, cancel_callback, semaphore);
245   XtManageChild( w );
246   XmStringFree( ms );
247   XmStringFree( ms2 );
248 }
249 
250 
251 
mb_result(widget,data,foo)252 static void mb_result( widget, data, foo )
253      Widget  widget;
254      char *data;
255      XmSelectionBoxCallbackStruct *foo;
256 {
257   char *string;
258   XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string);
259 
260   /* make sure MaxBytes is not too small or too large */
261   MaxBytes = max(DEFAULT_SNAPLEN, atoi(string));
262   MaxBytes = min(MaxBytes, MAX_SNAPLEN);
263 
264   SetLabel( mb_widget, ltos(MaxBytes) );
265   mb_semaphore = 0;
266 }
267 
268 
tl_result(widget,data,foo)269 static void tl_result( widget, data, foo )
270      Widget  widget;
271      char *data;
272      XmSelectionBoxCallbackStruct *foo;
273 {
274   char *string;
275   XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string);
276   TimeLimit = atoi(string);
277   if( TimeLimit <= 0 )
278     SetLabel( tl_widget, "infinite" );
279   else
280     SetLabel( tl_widget, ltos(TimeLimit) );
281   tl_semaphore = 0;
282 }
283 
nf_result(widget,data,foo)284 static void nf_result( widget, data, foo )
285      Widget  widget;
286      char *data;
287      XmSelectionBoxCallbackStruct *foo;
288 {
289   char *string;
290   XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string);
291   NumberOfFrames = atoi(string);
292   if( NumberOfFrames <= 0 ) {
293     NumberOfFrames = -1;
294     SetLabel( nf_widget, "infinite" );
295   } else
296     SetLabel( nf_widget, ltos(NumberOfFrames) );
297   nf_semaphore = 0;
298 }
299 
300 
dev_result(widget,data,foo)301 static void dev_result( widget, data, foo )
302      Widget  widget;
303      char *data;
304      XmSelectionBoxCallbackStruct *foo;
305 {
306   char *string;
307   static char buf[16];
308 
309   XmStringGetLtoR( foo->value, XmSTRING_DEFAULT_CHARSET, &string);
310   strcpy(buf, string);
311   Device = buf;
312   SetLabel( dev_widget, Device );
313   dev_semaphore = 0;
314 }
315 
316 
max_bytes_callback(widget,data,foo)317 static void max_bytes_callback( widget, data, foo )
318      Widget  widget;
319      caddr_t data;
320      caddr_t foo;
321 {
322   if( mb_semaphore )
323     return;
324   mb_semaphore = 1;
325   get_long( widget, MaxBytes,
326 	   "max_bytes",
327 	   "Maximum Number of Bytes to Capture Per Frame",
328 	   mb_result, &mb_semaphore );
329 }
330 
time_limit_callback(widget,data,foo)331 static void time_limit_callback( widget, data, foo )
332      Widget  widget;
333      caddr_t data;
334      caddr_t foo;
335 {
336   if( tl_semaphore )
337     return;
338   tl_semaphore = 1;
339   get_long( widget, TimeLimit,
340 	   "time_limit",
341 	   "Time Limit for Capture in Seconds",
342 	   tl_result, &tl_semaphore );
343 }
344 
num_frame_callback(widget,data,foo)345 static void num_frame_callback( widget, data, foo )
346      Widget  widget;
347      caddr_t data;
348      caddr_t foo;
349 {
350   if( nf_semaphore )
351     return;
352   nf_semaphore = 1;
353   get_long( widget, NumberOfFrames,
354 	   "number_frames",
355 	   "Number of Frames to Capture",
356 	   nf_result, &nf_semaphore );
357 }
358 
359 
prom_callback(widget,data,foo)360 static void prom_callback( widget, data, foo )
361      Widget  widget;
362      caddr_t data;
363      caddr_t foo;
364 {
365   if( pflag )
366     pflag = 0;
367   else
368     pflag = 1;
369   SetLabel( pr_widget, promstring[pflag]);
370 }
371 
done_callback(widget,data,foo)372 static void done_callback( widget, data, foo )
373      Widget  widget;
374      caddr_t data;
375      caddr_t foo;
376 {
377   XtUnmanageChild( bb_widget );
378 }
379 
cancel_callback(widget,data,foo)380 static void cancel_callback( widget, data, foo )
381      Widget  widget;
382      caddr_t data;
383      caddr_t foo;
384 {
385   *data = 0;
386   XtUnmanageChild(widget);
387 }
388 
dev_cancel(widget,data,foo)389 static void dev_cancel( widget, data, foo )
390      Widget  widget;
391      caddr_t data;
392      caddr_t foo;
393 {
394   dev_semaphore = 0;
395   XtUnmanageChild(widget);
396 }
397 
device_callback(parent,data,foo)398 static void device_callback( parent, data, foo )
399      Widget  parent;
400      caddr_t data;
401      caddr_t foo;
402 {
403   Widget w;
404   Arg args[2];
405   int n;
406   XmString ms, ms2;
407 
408   if( dev_semaphore )
409     return;
410   dev_semaphore = 1;
411 
412   ms = XmStringCreateSimple("Network Device Name");
413   if( Device )
414     ms2 = XmStringCreateSimple( Device );
415   else
416     ms2 = XmStringCreateSimple("");
417   n = 0;
418   XtSetArg (args[n], XmNselectionLabelString, ms); n++;
419   XtSetArg (args[n], XmNtextString, ms2); n++;
420   w = XmCreatePromptDialog( parent, "dev" ,args,n);
421   XtAddCallback( w, XmNokCallback, dev_result, (caddr_t)0);
422   XtAddCallback( w, XmNcancelCallback, dev_cancel, (caddr_t)0);
423   XtManageChild( w );
424   XmStringFree( ms );
425   XmStringFree( ms2 );
426 }
427 
428 
end_capture()429 static void end_capture()
430 {
431   if( if_fd >= 0)
432     wrapup(if_fd);
433   *StrPtr = 0;
434   sf_write_end();
435   exit(0);
436 }
437 
438 u_short DoneWithCapture;
439 
child_end()440 static void child_end()
441 {
442   int status;
443   FILE *fp;
444   int num;
445   char buf[512];
446 
447   if( Work) XtUnrealizeWidget( Work );
448   DoneWithCapture = 1;
449   signal( SIGCHLD, SIG_IGN);
450   (void)wait(&status);
451 /*  fprintf(stderr,"child exited with status %d\n",WEXITSTATUS(status)); */
452 
453   fp = fopen(StderrName,"r");
454   if( fp ) {
455     num = fread( buf, 1, sizeof(buf), fp );
456     if( num ) {
457       buf[num]='\0';
458       eprint( buf );
459     }
460     unlink( StderrName );
461     fclose( fp );
462   }
463   if( WEXITSTATUS(status) == 0 )
464     readfile( TempName );
465   unlink( TempName );
466 }
467 
468 /* semaphore */
469 static u_short Stopped=0;
470 
stop_capture_callback(widget,data,call_data)471 static void stop_capture_callback( widget, data, call_data )
472 Widget  widget;
473 caddr_t data;
474 caddr_t call_data;
475 {
476   Stopped = 1;
477   if( kill( (pid_t)data, SIGINT ) < 0 )
478     xperror("kill");
479   if( TimeoutID ) {
480     XtRemoveTimeOut( TimeoutID );
481     TimeoutID = 0;
482   }
483   Stopped = 0;
484 }
485 
timer_expired(data,id)486 static void timer_expired( data, id )
487 caddr_t data;
488 XtIntervalId *id;
489 {
490   if( Stopped )
491     return;
492   TimeoutID = 0;
493   (void)kill( (pid_t)data, SIGINT );
494 }
495 
capture_callback(widget,name,call_data)496 void capture_callback( widget, name, call_data )
497 Widget  widget;
498 char    *name;
499 caddr_t call_data;
500 {
501   u_long netmask, localnet;
502   struct bpf_program *bpf;
503   pid_t pid;
504   extern long thiszone;
505   extern int snaplen, Linktype, Precision;
506 
507   DoneWithCapture = 0;
508   Work = NULL;
509 
510   {
511     struct timeval now;
512     struct timezone tz;
513 
514     if (gettimeofday(&now, &tz) < 0) {
515       xperror("tcpdump: gettimeofday");
516       exit(1);
517     }
518     thiszone = tz.tz_minuteswest * -60;
519     if (localtime((time_t *)&now.tv_sec)->tm_isdst)
520       thiszone += 3600;
521   }
522   Precision = clock_sigfigs();
523 
524   /* this should come from the capture options */
525   /* default and max should be based on DLC */
526   snaplen = MaxBytes;
527 
528   /* pick a temporary name for stderr */
529   strcpy(StderrName,tmpnam(NULL));
530 
531   /* pick a temporary name to save frames to */
532   TempName = tmpnam(NULL);
533 
534   update_fstr(&Filter);
535 
536   (void)signal( SIGCHLD, child_end );
537 
538   if( (pid = fork()) < 0 ) {
539     eprint("Problem forking capture process");
540     return;
541   }
542   if( pid == 0 ) {
543 
544     /* send stderr to a temp file */
545     (void)freopen(StderrName,"w",stderr);
546 
547     if( Device == NULL ) {
548       Device = lookup_device();
549       if( Device == NULL ) {
550 	eprint("Can't find device");
551 	exit(1);
552       }
553     }
554     if_fd = initdevice( Device, pflag, &Linktype );
555     lookup_net( Device, &localnet, &netmask );
556     bpf = parse( Fstr, 1, Linktype, netmask );
557 
558     /* set up printf buffer */
559     StrPtr = strbuf = malloc(512);
560 
561     sf_write_init( TempName, Linktype, thiszone, snaplen, Precision );
562     (void)signal( SIGTERM, end_capture );
563     (void)signal( SIGINT, end_capture );
564     (void)signal( SIGHUP, end_capture );
565     readloop( NumberOfFrames, if_fd, bpf, sf_write );
566     end_capture();
567   } else {	/* parent */
568     XmString ms, ms_stop;
569     Arg args[2];
570     int n;
571 
572     if( TimeLimit > 0 )
573       TimeoutID = XtAddTimeOut( TimeLimit*1000, timer_expired, (caddr_t)pid );
574 
575     /* now create a dialog with "Stop" button */
576     ms = XmStringCreateSimple( "Capturing");
577     ms_stop = XmStringCreateSimple( "STOP" );
578     n = 0;
579     XtSetArg( args[n], XmNmessageString, ms ); n++;
580     XtSetArg( args[n], XmNokLabelString, ms_stop ); n++;
581     Work = XmCreateWorkingDialog( widget,"STOP",args,n );
582     RemoveDialButton( Work, XmDIALOG_HELP_BUTTON );
583     RemoveDialButton( Work, XmDIALOG_CANCEL_BUTTON );
584     XtAddCallback( Work, XmNokCallback,stop_capture_callback, (caddr_t)pid );
585 
586     if( !DoneWithCapture )
587       XtManageChild( Work );
588 
589     XmStringFree( ms );
590     XmStringFree( ms_stop );
591   }
592 }       /* capture_callback */
593 
594 
595