1 /*
2  * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
3  *               2002, 2003, 2004
4  *	Ohio University.
5  *
6  * ---
7  *
8  * Starting with the release of tcptrace version 6 in 2001, tcptrace
9  * is licensed under the GNU General Public License (GPL).  We believe
10  * that, among the available licenses, the GPL will do the best job of
11  * allowing tcptrace to continue to be a valuable, freely-available
12  * and well-maintained tool for the networking community.
13  *
14  * Previous versions of tcptrace were released under a license that
15  * was much less restrictive with respect to how tcptrace could be
16  * used in commercial products.  Because of this, I am willing to
17  * consider alternate license arrangements as allowed in Section 10 of
18  * the GNU GPL.  Before I would consider licensing tcptrace under an
19  * alternate agreement with a particular individual or company,
20  * however, I would have to be convinced that such an alternative
21  * would be to the greater benefit of the networking community.
22  *
23  * ---
24  *
25  * This file is part of Tcptrace.
26  *
27  * Tcptrace was originally written and continues to be maintained by
28  * Shawn Ostermann with the help of a group of devoted students and
29  * users (see the file 'THANKS').  The work on tcptrace has been made
30  * possible over the years through the generous support of NASA GRC,
31  * the National Science Foundation, and Sun Microsystems.
32  *
33  * Tcptrace is free software; you can redistribute it and/or modify it
34  * under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * Tcptrace is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with Tcptrace (in the file 'COPYING'); if not, write to the
45  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46  * MA 02111-1307 USA
47  *
48  * Author:	Shawn Ostermann
49  * 		School of Electrical Engineering and Computer Science
50  * 		Ohio University
51  * 		Athens, OH
52  *		ostermann@cs.ohiou.edu
53  *		http://www.tcptrace.org/
54  */
55 #include "tcptrace.h"
56 static char const GCC_UNUSED copyright[] =
57     "@(#)Copyright (c) 2004 -- Ohio University.\n";
58 static char const GCC_UNUSED rcsid[] =
59     "@(#)$Header$";
60 
61 #include "file_formats.h"
62 #include "modules.h"
63 #include "version.h"
64 
65 
66 /* version information */
67 char *tcptrace_version = VERSION;
68 
69 
70 /* local routines */
71 static void Args(void);
72 static void ModulesPerNonTCPUDP(struct ip *pip, void *plast);
73 static void ModulesPerPacket(struct ip *pip, tcp_pair *ptp, void *plast);
74 static void ModulesPerUDPPacket(struct ip *pip, udp_pair *pup, void *plast);
75 static void ModulesPerConn(tcp_pair *ptp);
76 static void ModulesPerUDPConn(udp_pair *pup);
77 static void ModulesPerFile(char *filename);
78 static void DumpFlags(void);
79 static void ExplainOutput(void);
80 static void FinishModules(void);
81 static void Formats(void);
82 static void Help(char *harg);
83 static void Hints(void);
84 static void ListModules(void);
85 static void UsageModules(void);
86 static void LoadModules(int argc, char *argv[]);
87 static void CheckArguments(int *pargc, char *argv[]);
88 static void ParseArgs(char *argsource, int *pargc, char *argv[]);
89 static int  ParseExtendedOpt(char *argsource, char *arg);
90 static void ParseExtendedBool(char *argsource, char *arg);
91 static void ParseExtendedVar(char *argsource, char *arg);
92 static void ProcessFile(char *filename);
93 static void QuitSig(int signum);
94 static void Usage(void);
95 static void BadArg(char *argsource, char *format, ...);
96 static void Version(void);
97 static char *FileToBuf(char *filename);
98 
99 
100 /* option flags and default values */
101 Bool colorplot = TRUE;
102 Bool dump_rtt = FALSE;
103 Bool graph_rtt = FALSE;
104 Bool graph_tput = FALSE;
105 Bool graph_tsg = FALSE;
106 Bool graph_segsize = FALSE;
107 Bool graph_owin = FALSE;
108 Bool graph_tline = FALSE;
109 Bool graph_recvwin = FALSE;
110 Bool hex = TRUE;
111 Bool ignore_non_comp = FALSE;
112 Bool dump_packet_data = FALSE;
113 Bool print_rtt = FALSE;
114 Bool print_owin = FALSE;
115 Bool printbrief = TRUE;
116 Bool printsuppress = FALSE;
117 Bool printem = FALSE;
118 Bool printallofem = FALSE;
119 Bool printticks = FALSE;
120 Bool warn_ooo = FALSE;
121 Bool warn_printtrunc = FALSE;
122 Bool warn_printbadmbz = FALSE;
123 Bool warn_printhwdups = FALSE;
124 Bool warn_printbadcsum = FALSE;
125 Bool warn_printbad_syn_fin_seq = FALSE;
126 Bool docheck_hw_dups = TRUE;
127 Bool save_tcp_data = FALSE;
128 Bool graph_time_zero = FALSE;
129 Bool graph_seq_zero = FALSE;
130 Bool print_seq_zero = FALSE;
131 Bool graph_zero_len_pkts = TRUE;
132 Bool plot_tput_instant = TRUE;
133 Bool filter_output = FALSE;
134 Bool show_title = TRUE;
135 Bool show_rwinline = TRUE;
136 Bool do_udp = FALSE;
137 Bool resolve_ipaddresses = TRUE;
138 Bool resolve_ports = TRUE;
139 Bool verify_checksums = FALSE;
140 Bool triple_dupack_allows_data = FALSE;
141 Bool run_continuously = FALSE;
142 Bool xplot_all_files = FALSE;
143 Bool conn_num_threshold = FALSE;
144 Bool ns_hdrs = TRUE;
145 Bool dup_ack_handling = TRUE;
146 Bool csv = FALSE;
147 Bool tsv = FALSE;
148 u_long remove_live_conn_interval = REMOVE_LIVE_CONN_INTERVAL;
149 u_long nonreal_live_conn_interval = NONREAL_LIVE_CONN_INTERVAL;
150 u_long remove_closed_conn_interval = REMOVE_CLOSED_CONN_INTERVAL;
151 u_long update_interval = UPDATE_INTERVAL;
152 u_long max_conn_num = MAX_CONN_NUM;
153 int debug = 0;
154 u_long beginpnum = 0;
155 u_long endpnum = 0;
156 u_long pnum = 0;
157 u_long ctrunc = 0;
158 u_long bad_ip_checksums = 0;
159 u_long bad_tcp_checksums = 0;
160 u_long bad_udp_checksums = 0;
161 
162 /* extended variables with values */
163 char *output_file_dir = NULL;
164 char *output_file_prefix = NULL;
165 char *xplot_title_prefix = NULL;
166 char *xplot_args = NULL;
167 char *sv = NULL;
168 /* globals */
169 struct timeval current_time;
170 int num_modules = 0;
171 char *ColorNames[NCOLORS] =
172 {"green", "red", "blue", "yellow", "purple", "orange", "magenta", "pink"};
173 char *comment;
174 
175 /* locally global variables */
176 static u_long filesize = 0;
177 char **filenames = NULL;
178 int num_files = 0;
179 u_int numfiles;
180 char *cur_filename;
181 static char *progname;
182 char *output_filename = NULL;
183 static char *update_interval_st = NULL;
184 static char *max_conn_num_st = NULL;
185 static char *live_conn_interval_st = NULL;
186 static char *nonreal_conn_interval_st = NULL;
187 static char *closed_conn_interval_st = NULL;
188 
189 /* for elapsed processing time */
190 struct timeval wallclock_start;
191 struct timeval wallclock_finished;
192 
193 
194 /* first and last packet timestamp */
195 timeval first_packet = {0,0};
196 timeval last_packet = {0,0};
197 
198 
199 /* extended boolean options */
200 static struct ext_bool_op {
201     char *bool_optname;
202     Bool *bool_popt;
203     Bool bool_default;
204     char *bool_descr;
205 } extended_bools[] = {
206     {"showsacks", &show_sacks,  TRUE,
207      "show SACK blocks on time sequence graphs"},
208     {"showrexmit", &show_rexmit,  TRUE,
209      "mark retransmits on time sequence graphs"},
210     {"showoutorder", &show_out_order,  TRUE,
211      "mark out-of-order on time sequence graphs"},
212     {"showzerowindow", &show_zero_window,  TRUE,
213      "mark zero windows on time sequence graphs"},
214     {"showurg", &show_urg,  TRUE,
215      "mark packets with URGENT bit set on the time sequence graphs"},
216     {"showrttdongles", &show_rtt_dongles,  TRUE,
217      "mark non-RTT-generating ACKs with special symbols"},
218     {"showdupack3", &show_triple_dupack,  TRUE,
219      "mark triple dupacks on time sequence graphs"},
220     {"showzerolensegs", &graph_zero_len_pkts,  TRUE,
221      "show zero length packets on time sequence graphs"},
222     {"showzwndprobes", &show_zwnd_probes, TRUE,
223      "show zero window probe packets on time sequence graphs"},
224     {"showtitle", &show_title,  TRUE,
225      "show title on the graphs"},
226     {"showrwinline", &show_rwinline,  TRUE,
227      "show yellow receive-window line in owin graphs"},
228     {"graphrecvwin", &graph_recvwin,  TRUE,
229      "create recv window graph"},
230     {"res_addr", &resolve_ipaddresses,  TRUE,
231      "resolve IP addresses into names (may be slow)"},
232     {"res_port", &resolve_ports,  TRUE,
233      "resolve port numbers into names"},
234     {"checksum", &verify_checksums,  TRUE,
235      "verify IP and TCP checksums"},
236     {"dupack3_data", &triple_dupack_allows_data, TRUE,
237      "count a duplicate ACK carrying data as a triple dupack"},
238     {"check_hwdups", &docheck_hw_dups, TRUE,
239      "check for 'hardware' dups"},
240     {"warn_ooo", &warn_ooo,  TRUE,
241      "print warnings when packets timestamps are out of order"},
242     {"warn_printtrunc", &warn_printtrunc,  TRUE,
243      "print warnings when packets are too short to analyze"},
244     {"warn_printbadmbz", &warn_printbadmbz, TRUE,
245      "print warnings when MustBeZero TCP fields are NOT 0"},
246     {"warn_printhwdups", &warn_printhwdups, TRUE,
247      "print warnings for hardware duplicates"},
248     {"warn_printbadcsum", &warn_printbadcsum, TRUE,
249      "print warnings when packets with bad checksums"},
250     {"warn_printbad_syn_fin_seq", &warn_printbad_syn_fin_seq, TRUE,
251      "print warnings when SYNs or FINs rexmitted with different sequence numbers"},
252     {"dump_packet_data", &dump_packet_data, TRUE,
253      "print all packets AND dump the TCP/UDP data"},
254     {"continuous", &run_continuously, TRUE,
255      "run continuously and don't provide a summary"},
256     {"print_seq_zero", &print_seq_zero, TRUE,
257      "print sequence numbers as offset from initial sequence number"},
258     {"limit_conn_num", &conn_num_threshold, TRUE,
259      "limit the maximum number of connections kept at a time in real-time mode"},
260     {"xplot_all_files", &xplot_all_files, TRUE,
261      "display all generated xplot files at the end"},
262     {"ns_hdrs", &ns_hdrs, TRUE,
263      "assume that ns has the useHeaders_flag true (uses IP+TCP headers)"},
264     {"csv", &csv, TRUE,
265      "display the long output as comma separated values"},
266     {"tsv", &tsv, TRUE,
267      "display the long output as tab separated values"},
268     {"turn_off_BSD_dupack", &dup_ack_handling, FALSE,
269      "turn of the BSD version of the duplicate ack handling"},
270 
271 };
272 #define NUM_EXTENDED_BOOLS (sizeof(extended_bools) / sizeof(struct ext_bool_op))
273 
274 
275 /* extended variable verification routines */
276 static u_long VerifyPositive(char *varname, char *value);
277 static void VerifyUpdateInt(char *varname, char *value);
278 static void VerifyMaxConnNum(char *varname, char *value);
279 static void VerifyLiveConnInt(char *varname, char *value);
280 static void VerifyNonrealLiveConnInt(char *varname, char*value);
281 static void VerifyClosedConnInt(char *varname, char *value);
282 
283 /* extended variable options */
284 /* they must all be strings */
285 static struct ext_var_op {
286     char *var_optname;		/* what it's called when you set it */
287     char **var_popt;		/* the variable itself */
288     void (*var_verify)(char *varname,
289 		       char *value);
290 				/* function to call to verify that the
291 				   value is OK (if non-null) */
292     char *var_descr;		/* variable description */
293 } extended_vars[] = {
294     {"output_dir", &output_file_dir, NULL,
295      "directory where all output files are placed"},
296     {"output_prefix", &output_file_prefix, NULL,
297      "prefix all output files with this string"},
298     {"xplot_title_prefix", &xplot_title_prefix, NULL,
299      "prefix to place in the titles of all xplot files"},
300     {"update_interval", &update_interval_st, VerifyUpdateInt,
301      "time interval for updates in real-time mode"},
302     {"max_conn_num", &max_conn_num_st, VerifyMaxConnNum,
303      "maximum number of connections to keep at a time in real-time mode"},
304     {"remove_live_conn_interval", &live_conn_interval_st, VerifyLiveConnInt,
305      "idle time after which an open connection is removed in real-time mode"},
306     {"endpoint_reuse_interval", &nonreal_conn_interval_st, VerifyNonrealLiveConnInt,
307      "time interval of inactivity after which an open connection is considered closed"},
308      {"remove_closed_conn_interval", &closed_conn_interval_st, VerifyClosedConnInt,
309      "time interval after which a closed connection is removed in real-time mode"},
310     {"xplot_args", &xplot_args, NULL,
311      "arguments to pass to xplot, if we are calling xplot from here"},
312     {"sv", &sv, NULL,
313      "separator to use for long output with <STR>-separated-values"},
314 
315 };
316 #define NUM_EXTENDED_VARS (sizeof(extended_vars) / sizeof(struct ext_var_op))
317 
318 // Extended option verification routines
319 static void Ignore(char *argsource, char *opt);
320 static void GrabOnly(char *argsource, char *opt);
321 static void IgnoreUDP(char *argsource, char *opt);
322 static void GrabOnlyUDP(char *argsource, char *opt);
323 
324 // Extended options to allow --iudp and --oudp to be able to
325 // specifically ignore or output UDP connections.
326 // For sake of clarity, options --itcp and --otcp shall also be added
327 // to do the same job done by -i and -o options currently.
328 static struct ext_opt {
329      char *opt_name;  // what it is called
330      void (*opt_func) (char *argsource, char *opt);
331      char *opt_descr;
332 } extended_options[] = {
333      {"iTCP",Ignore,"ignore specific TCP connections, same as -i"},
334      {"oTCP",GrabOnly,"only specific TCP connections, same as -o"},
335      {"iUDP",IgnoreUDP,"ignore specific UDP connections"},
336      {"oUDP",GrabOnlyUDP,"only specific UDP connections"}
337 
338 };
339 #define NUM_EXTENDED_OPTIONS (sizeof(extended_options)/sizeof(struct ext_opt))
340 
341 static void
Help(char * harg)342 Help(
343     char *harg)
344 {
345     if (harg && *harg && strncmp(harg,"arg",3) == 0) {
346 	Args();
347     } else if (harg && *harg && strncmp(harg,"xarg",3) == 0) {
348 	UsageModules();
349     } else if (harg && *harg && strncmp(harg,"filt",4) == 0) {
350 	HelpFilter();
351     } else if (harg && *harg && strncmp(harg,"conf",4) == 0) {
352 	Formats();
353 	CompFormats();
354 	ListModules();
355     } else if (harg && *harg && strncmp(harg,"out",3) == 0) {
356 	ExplainOutput();
357     } else if (harg && *harg &&
358 	       ((strncmp(harg,"hint",4) == 0) || (strncmp(harg,"int",3) == 0))) {
359 	Hints();
360     } else {
361 	fprintf(stderr,"\
362 For help on specific topics, try:  \n\
363   -hargs    tell me about the program's arguments  \n\
364   -hxargs   tell me about the module arguments  \n\
365   -hconfig  tell me about the configuration of this binary  \n\
366   -houtput  explain what the output means  \n\
367   -hfilter  output filtering help  \n\
368   -hhints   usage hints  \n");
369     }
370 
371     fprintf(stderr,"\n");
372     Version();
373     exit(0);
374 }
375 
376 
377 
378 static void
BadArg(char * argsource,char * format,...)379 BadArg(
380     char *argsource,
381     char *format,
382     ...)
383 {
384     va_list ap;
385 
386     fprintf(stderr,"Argument error");
387     if (argsource)
388 	fprintf(stderr," (from %s)", argsource);
389     fprintf(stderr,": ");
390 
391     va_start(ap,format);
392     vfprintf(stderr,format,ap);
393     va_end(ap);
394 
395     Usage();
396 }
397 
398 
399 
400 static void
Usage(void)401 Usage(void)
402 {
403     fprintf(stderr,"usage: %s [args...]* dumpfile [more files]*\n",
404 	    progname);
405 
406     Help(NULL);
407 
408     exit(-2);
409 }
410 
411 
412 static void
ExplainOutput(void)413 ExplainOutput(void)
414 {
415     fprintf(stderr,"\n\
416 OK, here's a sample output (using -l) and what each line means:\n\
417 \n\
418 1 connection traced:\n\
419               ####   how many distinct TCP connections did I see pieces of\n\
420 13 packets seen, 13 TCP packets traced\n\
421               ####   how many packets did I see, how many did I trace\n\
422 connection 1:\n\
423               #### I'll give you a separate block for each connection,\n\
424               #### here's the first one\n\
425 	host a:        132.235.3.133:1084\n\
426 	host b:        132.235.1.2:79 \n\
427               #### Each connection has two hosts.  To shorten output\n\
428               #### and file names, I just give them letters.\n\
429               #### Connection 1 has hosts 'a' and 'b', connection 2\n\
430               #### has hosts 'c' and 'd', etc.\n\
431 	complete conn: yes\n\
432               #### Did I see the starting FINs and closing SYNs?  Was it reset?\n\
433 	first packet:  Wed Jul 20 16:40:30.688114\n\
434               #### At what time did I see the first packet from this connection?\n\
435 	last packet:   Wed Jul 20 16:40:41.126372\n\
436               #### At what time did I see the last packet from this connection?\n\
437 	elapsed time:  0:00:10.438257\n\
438               #### Elapsed time, first packet to last packet\n\
439 	total packets: 13\n\
440               #### total packets this connection\n\
441 \n\
442               #### ... Now, there's two columns of output (TCP is\n\
443               #### duplex) one for each direction of packets.\n\
444               #### I'll just explain one column...\n\
445    a->b:			      b->a:\n\
446      total packets:             7           total packets:             6\n\
447               #### packets sent in each direction\n\
448      ack pkts sent:             6           ack pkts sent:             6\n\
449               #### how many of the packets contained a valid ACK\n\
450      unique bytes sent:        11           unique bytes sent:      1152\n\
451               #### how many data bytes were sent (not counting retransmissions)\n\
452      actual data pkts:          2           actual data pkts:          1\n\
453               #### how many packets did I see that contained any amount of data\n\
454      actual data bytes:        11           actual data bytes:      1152\n\
455               #### how many data bytes did I see (including retransmissions)\n\
456      rexmt data pkts:           0           rexmt data pkts:           0\n\
457               #### how many data packets were retransmissions\n\
458      rexmt data bytes:          0           rexmt data bytes:          0\n\
459               #### how many bytes were retransmissions\n\
460      outoforder pkts:           0           outoforder pkts:           0\n\
461               #### how many packets were out of order (or I didn't see the first transmit!)\n\
462      SYN/FIN pkts sent:       1/1           SYN/FIN pkts sent:       1/1\n\
463               #### how many SYNs and FINs were sent in each direction\n\
464      mss requested:          1460 bytes     mss requested:          1460 bytes\n\
465               #### What what the requested Maximum Segment Size\n\
466      max segm size:             9 bytes     max segm size:          1152 bytes\n\
467               #### What was the largest segment that I saw\n\
468      min segm size:             2 bytes     min segm size:          1152 bytes\n\
469               #### What was the smallest segment that I saw\n\
470      avg segm size:             5 bytes     avg segm size:          1150 bytes\n\
471               #### What was the average segment that I saw\n\
472      max win adv:            4096 bytes     max win adv:            4096 bytes\n\
473               #### What was the largest window advertisement that I sent\n\
474      min win adv:            4096 bytes     min win adv:            4085 bytes\n\
475               #### What was the smallest window advertisement that I sent\n\
476      zero win adv:              0 times     zero win adv:              0 times\n\
477               #### How many times did I sent a zero-sized window advertisement\n\
478      avg win adv:            4096 bytes     avg win adv:            4092 bytes\n\
479               #### What was the average window advertisement that I sent\n\
480      initial window:            9 bytes     initial window:         1152 bytes\n\
481               #### How many bytes in the first window (before the first ACK)\n\
482      initial window:            1 pkts      initial window:            1 pkts\n\
483               #### How many packets in the first window (before the first ACK)\n\
484      throughput:                1 Bps       throughput:              110 Bps\n\
485               #### What was the data throughput (Bytes/second)\n\
486      ttl stream length:        11 bytes     ttl stream length:      1152 bytes\n\
487               #### What was the total length of the stream (from FIN to SYN)\n\
488               #### Note that this might be larger than unique data bytes because\n\
489               #### I might not have captured every segment!!!\n\
490      missed data:               0 bytes     missed data:               0 bytes\n\
491               #### How many bytes of data were in the stream that I didn't see?\n\
492      RTT samples:               2           RTT samples:               1\n\
493               #### How many ACK's could I use to get an RTT sample\n\
494      RTT min:                45.9 ms        RTT min:                19.4 ms\n\
495               #### What was the smallest RTT that I saw\n\
496      RTT max:               199.0 ms        RTT max:                19.4 ms\n\
497               #### What was the largest RTT that I saw\n\
498      RTT avg:               122.5 ms        RTT avg:                19.4 ms\n\
499               #### What was the average RTT that I saw\n\
500      RTT stdev:               0.0 ms        RTT stdev:               0.0 ms\n\
501               #### What was the standard deviation of the RTT that I saw\n\
502      segs cum acked:            0           segs cum acked:            0\n\
503               #### How many segments were cumulatively ACKed (the ACK that I saw\n\
504 	      #### was for a later segment.  Might be a lost ACK or a delayed ACK\n\
505      duplicate acks:            2           duplicate acks:            0\n\
506               #### How many duplicate ACKs did I see\n\
507      max # retrans:             0           max # retrans:             0\n\
508               #### What was the most number of times that a single segment\n\
509               #### was retransmitted\n\
510      min retr time:           0.0 ms        min retr time:           0.0 ms\n\
511               #### What was the minimum time between retransmissions of a\n\
512               #### single segment\n\
513      max retr time:           0.0 ms        max retr time:           0.0 ms\n\
514               #### What was the maximum time between retransmissions of a\n\
515               #### single segment\n\
516      avg retr time:           0.0 ms        avg retr time:           0.0 ms\n\
517               #### What was the average time between retransmissions of a\n\
518               #### single segment\n\
519      sdv retr time:           0.0 ms        sdv retr time:           0.0 ms\n\
520               #### What was the stdev between retransmissions of a\n\
521               #### single segment\n\
522 ");
523 }
524 
525 
526 
527 static void
Hints(void)528 Hints(void)
529 {
530     fprintf(stderr,"\n\
531 Hints (in no particular order):\n\
532 For the first run through a file, just use \"tcptrace file\" to see\n\
533    what's there\n\
534 For large files, use \"-t\" and I'll give you progress feedback as I go\n\
535 If there's a lot of hosts, particularly if they're non-local, use \"-n\"\n\
536    to disable address to name mapping which can be very slow\n\
537 If you're graphing results and only want the information for a few conns,\n\
538    from a large file, use the -o flag, as in \"tcptrace -o3,4,5 -o8-11\" to\n\
539    only process connections 3,4,5, and 8 through 11.\n\
540    Alternately, the '-oFILE' option allows you to write the connection\n\
541    list into a file using some other program (or the file PF from -f)\n\
542 Make sure the snap length in the packet grabber is big enough.\n\
543      Ethernet headers are 14 bytes, as are several others\n\
544      IPv4 headers are at least 20 bytes, but can be as large as 64 bytes\n\
545      TCP headers are at least 20 bytes, but can be as large as 64 bytes\n\
546    Therefore, if you want to be SURE that you see all of the options,\n\
547    make sure that you set the snap length to 14+64+64=142 bytes.  If\n\
548    I'm not sure, I usually use 128 bytes.  If you're SURE that there are no\n\
549    options (TCP usually has some), you still need at least 54 bytes.\n\
550 Compress trace files using gzip, I can uncompress them on the fly\n\
551 Stuff arguments that you always use into either the tcptrace resource file\n\
552    ($HOME/%s) or the envariable %s.  If you need to turn\n\
553    them off again from the command line, you can use\n\
554    the \"+\" option flag.\n\
555 ", TCPTRACE_RC_FILE, TCPTRACE_ENVARIABLE);
556 }
557 
558 
559 static void
Args(void)560 Args(void)
561 {
562     int i;
563 
564     fprintf(stderr,"\n\
565 Note: these options are first read from the file $HOME/%s\n\
566   (if it exists), and then from the environment variable %s\n\
567   (if it exists), and finally from the command line\n\
568 ", TCPTRACE_RC_FILE, TCPTRACE_ENVARIABLE);
569     fprintf(stderr,"\n\
570 Output format options\n\
571   -b      brief output format\n\
572   -l      long output format\n\
573   -r      print rtt statistics (slower for large files)\n\
574   -W      report on estimated congestion window (not generally useful)\n\
575   -q      no output (if you just want modules output)\n\
576 Graphing options\n\
577   -T      create throughput graph[s], (average over 10 segments, see -A)\n\
578   -R      create rtt sample graph[s]\n\
579   -S      create time sequence graph[s]\n\
580   -N      create owin graph[s] (_o_utstanding data on _N_etwork)\n\
581   -F      create segsize graph[s]\n\
582   -L      create time line graph[s]\n\
583   -G	  create ALL graphs\n\
584 Output format detail options\n\
585   -D      print in decimal\n\
586   -X      print in hexadecimal\n\
587   -n      don't resolve host or service names (much faster)\n\
588   -s      use short names (list \"picard.cs.ohiou.edu\" as just \"picard\")\n\
589 Connection filtering options\n\
590   -iN     ignore connection N (can use multiple times)\n\
591   -oN[-M] only connection N (or N through M).  Arg can be used many times.\n\
592           If N is a file rather than a number, read list from file instead.\n\
593   -c      ignore non-complete connections (didn't see syn's and fin's)\n\
594   -BN     first segment number to analyze (default 1)\n\
595   -EN     last segment number to analyze (default last in file)\n\
596 Graphing detail options\n\
597   -C      produce color plot[s]\n\
598   -M      produce monochrome (b/w) plot[s]\n\
599   -AN     Average N segments for throughput graphs, default is 10\n\
600   -z      zero axis options\n\
601     -z      plot time axis from 0 rather than wall clock time (backward compat)\n\
602     -zx     plot time axis from 0 rather than wall clock time\n\
603     -zy     plot sequence numbers from 0 (time sequence graphs only)\n\
604     -zxy    plot both axes from 0\n\
605   -y      omit the (yellow) instantaneous throughput points in tput graph\n\
606 Misc options\n\
607   -Z      dump raw rtt sample times to file[s]\n\
608   -p      print all packet contents (can be very long)\n\
609   -P      print packet contents for selected connections\n\
610   -t      'tick' off the packet numbers as a progress indication\n\
611   -fEXPR  output filtering (see -hfilter)\n\
612   -v      print version information and exit\n\
613   -w      print various warning messages\n\
614   -d      whistle while you work (enable debug, use -d -d for more output)\n\
615   -e      extract contents of each TCP stream into file\n\
616   -h      print help messages\n\
617   -u      perform (minimal) UDP analysis too\n\
618   -Ofile  dump matched packets to tcpdump file 'file'\n\
619   +[v]    reverse the setting of the -[v] flag (for booleans)\n\
620 Dump File Names\n\
621   Anything else in the arguments is taken to be one or more filenames.\n\
622   The files can be compressed, see compress.h for configuration.\n\
623   If the dump file name is 'stdin', then we read from standard input\n\
624     rather than from a file\n\
625 ");
626 
627     fprintf(stderr,"\nExtended boolean options\n");
628     fprintf(stderr," (unambiguous prefixes also work)\n");
629     for (i=0; i < NUM_EXTENDED_BOOLS; ++i) {
630 	struct ext_bool_op *pbop = &extended_bools[i];
631 	fprintf(stderr,"  --%-20s %s %s\n",
632 		pbop->bool_optname, pbop->bool_descr,
633 		(*pbop->bool_popt == pbop->bool_default)?"(default)":"");
634 	fprintf(stderr,"  --no%-18s DON'T %s %s\n",
635 		pbop->bool_optname, pbop->bool_descr,
636 		(*pbop->bool_popt != pbop->bool_default)?"(default)":"");
637     }
638 
639     fprintf(stderr,"\nExtended variable options\n");
640     fprintf(stderr," (unambiguous prefixes also work)\n");
641     for (i=0; i < NUM_EXTENDED_VARS; ++i) {
642 	char buf[256];		/* plenty large, but checked below with strncpy */
643 	struct ext_var_op *pvop = &extended_vars[i];
644 	strncpy(buf,pvop->var_optname,sizeof(buf)-10);
645 	strcat(buf,"=\"STR\"");
646 	fprintf(stderr,"  --%-20s %s (default: '%s')\n",
647 		buf,
648 		pvop->var_descr,
649 		(*pvop->var_popt)?*pvop->var_popt:"<NULL>");
650     }
651 
652     fprintf(stderr,"\n\
653 Module options\n\
654   -xMODULE_SPECIFIC  (see -hxargs for details)\n\
655 ");
656 }
657 
658 
659 
660 static void
Version(void)661 Version(void)
662 {
663     fprintf(stderr,"Version: %s\n", tcptrace_version);
664     fprintf(stderr,"  Compiled by '%s' at '%s' on machine '%s'\n",
665 	    built_bywhom, built_when, built_where);
666 }
667 
668 
669 static void
Formats(void)670 Formats(void)
671 {
672     int i;
673 
674     fprintf(stderr,"Supported Input File Formats:\n");
675     for (i=0; i < NUM_FILE_FORMATS; ++i)
676 	fprintf(stderr,"\t%-15s  %s\n",
677 		file_formats[i].format_name,
678 		file_formats[i].format_descr);
679    fprintf(stderr,
680 	   "Try the tethereal program from the ethereal project to see if\n"
681 	   "it can understand this capture format. If so, you may use \n"
682 	   "tethereal to convert it to a tcpdump format file as in :\n"
683 	   "\t tethereal -r inputfile -w outputfile\n"
684 	   "and feed the outputfile to tcptrace\n");
685 }
686 
687 
688 static void
ListModules(void)689 ListModules(void)
690 {
691     int i;
692 
693     fprintf(stderr,"Included Modules:\n");
694     for (i=0; i < NUM_MODULES; ++i) {
695 	fprintf(stderr,"  %-15s  %s\n",
696 		modules[i].module_name, modules[i].module_descr);
697 /* 	if (modules[i].module_usage) { */
698 /* 	    fprintf(stderr,"    usage:\n"); */
699 /* 	    (*modules[i].module_usage)(); */
700 /* 	} */
701     }
702 }
703 
704 
705 static void
UsageModules(void)706 UsageModules(void)
707 {
708     int i;
709 
710     for (i=0; i < NUM_MODULES; ++i) {
711 	fprintf(stderr," Module %s:\n", modules[i].module_name);
712 	if (modules[i].module_usage) {
713 	    fprintf(stderr,"    usage:\n");
714 	    (*modules[i].module_usage)();
715 	}
716     }
717 }
718 
719 
720 
721 int
main(int argc,char * argv[])722 main(
723     int argc,
724     char *argv[])
725 {
726     int i;
727     double etime;
728 
729     if (argc == 1)
730 	Help(NULL);
731 
732     /* initialize internals */
733     trace_init();
734     udptrace_init();
735     plot_init();
736 
737     /* let modules start first */
738     LoadModules(argc,argv);
739 
740     /* parse the flags */
741     CheckArguments(&argc,argv);
742 
743     /* Used with <SP>-separated-values,
744      * prints a '#' before each header line if --csv/--tsv is requested.
745      */
746    comment = (char *)malloc(sizeof(char *) * 2);
747    memset(comment, 0, sizeof(comment));
748    if(csv || tsv || (sv != NULL))
749      snprintf(comment, sizeof(comment), "#");
750 
751     /* optional UDP */
752 //    if (do_udp)
753 //	udptrace_init();
754 
755   //  if (run_continuously) {
756     //  trace_init();
757     //}
758 
759     /* get starting wallclock time */
760     gettimeofday(&wallclock_start, NULL);
761 
762     num_files = argc;
763     printf("%s%d arg%s remaining, starting with '%s'\n",
764 	   comment,
765 	   num_files,
766 	   num_files>1?"s":"",
767 	   filenames[0]);
768 
769 
770 
771     if (debug>1)
772 	DumpFlags();
773 
774     /* knock, knock... */
775     printf("%s%s\n\n", comment, VERSION);
776 
777     /* read each file in turn */
778     numfiles = argc;
779     for (i=0; i < argc; ++i) {
780 	if (debug || (numfiles > 1)) {
781 	    if (argc > 1)
782 		printf("%sRunning file '%s' (%d of %d)\n", comment, filenames[i], i+1, numfiles);
783 	    else
784 		printf("%sRunning file '%s'\n", comment, filenames[i]);
785 	}
786 
787 	/* do the real work */
788 	ProcessFile(filenames[i]);
789     }
790 
791     /* clean up output */
792     if (printticks)
793 	printf("\n");
794 
795     /* get ending wallclock time */
796     gettimeofday(&wallclock_finished, NULL);
797 
798     /* general output */
799     fprintf(stdout, "%s%lu packets seen, %lu TCP packets traced",
800 	    comment, pnum, tcp_trace_count);
801     if (do_udp)
802 	fprintf(stdout,", %lu UDP packets traced", udp_trace_count);
803     fprintf(stdout,"\n");
804 
805     /* processing time */
806     etime = elapsed(wallclock_start,wallclock_finished);
807     fprintf(stdout, "%selapsed wallclock time: %s, %d pkts/sec analyzed\n",
808 	    comment,
809 	    elapsed2str(etime),
810 	    (int)((double)pnum/(etime/1000000)));
811 
812     /* actual tracefile times */
813     etime = elapsed(first_packet,last_packet);
814     fprintf(stdout,"%strace %s elapsed time: %s\n",
815 	    comment,
816 	    (num_files==1)?"file":"files",
817 	    elapsed2str(etime));
818     if (debug) {
819 	fprintf(stdout,"%s\tfirst packet:  %s\n", comment, ts2ascii(&first_packet));
820 	fprintf(stdout,"%s\tlast packet:   %s\n", comment, ts2ascii(&last_packet));
821     }
822     if (verify_checksums) {
823 	fprintf(stdout,"%sbad IP checksums:  %ld\n", comment, bad_ip_checksums);
824 	fprintf(stdout,"%sbad TCP checksums: %ld\n", comment, bad_tcp_checksums);
825 	if (do_udp)
826 	    fprintf(stdout,"%sbad UDP checksums: %ld\n", comment, bad_udp_checksums);
827     }
828 
829     /* close files, cleanup, and etc... */
830     trace_done();
831     udptrace_done();
832 
833     FinishModules();
834     plotter_done();
835 
836     exit(0);
837 }
838 
839 
840 static void
ProcessFile(char * filename)841 ProcessFile(
842     char *filename)
843 {
844     pread_f *ppread;
845     int ret;
846     struct ip *pip;
847     struct tcphdr *ptcp;
848     int phystype;
849     void *phys;  /* physical transport header */
850     tcp_pair *ptp;
851     int fix;
852     int len;
853     int tlen;
854     void *plast;
855     struct stat str_stat;
856     long int location = 0;
857     u_long fpnum = 0;
858     Bool is_stdin = 0;
859     static int file_count = 0;
860 
861     /* share the current file name */
862     cur_filename = filename;
863 
864 #ifdef __WIN32
865     /* If the file is compressed, exit (Windows version does not support compressed dump files) */
866     if (CompOpenHeader(filename) == (FILE *)-1) {
867 	exit(-1);
868     }
869 #else
870     /* open the file header */
871     if (CompOpenHeader(filename) == NULL) {
872 	exit(-1);
873     }
874 #endif /* __WIN32 */
875 
876     /* see how big the file is */
877     if (FileIsStdin(filename)) {
878 	filesize = 1;
879 	is_stdin = 1;
880     } else {
881 	if (stat(filename,&str_stat) != 0) {
882 	    perror("stat");
883 	    exit(1);
884 	}
885 	filesize = str_stat.st_size;
886     }
887 
888     /* determine which input file format it is... */
889     ppread = NULL;
890     if (debug>1)
891 	printf("NUM_FILE_FORMATS: %u\n", (unsigned)NUM_FILE_FORMATS);
892     for (fix=0; fix < NUM_FILE_FORMATS; ++fix) {
893 	if (debug)
894 	    fprintf(stderr,"Checking for file format '%s' (%s)\n",
895 		    file_formats[fix].format_name,
896 		    file_formats[fix].format_descr);
897 	rewind(stdin);
898        	ppread = (*file_formats[fix].test_func)(filename);
899 	if (ppread) {
900 	    if (debug)
901                 fprintf(stderr,"File format is '%s' (%s)\n",
902 	                file_formats[fix].format_name,
903 	                file_formats[fix].format_descr);
904 	    break;
905 	} else if (debug) {
906 	    fprintf(stderr,"File format is NOT '%s'\n",
907 		    file_formats[fix].format_name);
908 	}
909     }
910 
911     /* if we haven't found a reader, then we can't continue */
912     if (ppread == NULL) {
913 	int count = 0;
914 
915 	fprintf(stderr,"Unknown input file format\n");
916 	Formats();
917 
918 	/* check for ASCII, a common problem */
919 	rewind(stdin);
920 	while (1) {
921 	    int ch;
922 	    if ((ch = getchar()) == EOF)
923 		break;
924 	    if (!isprint(ch))
925 		break;
926 	    if (++count >= 20) {
927 		/* first 20 are all ASCII */
928 		fprintf(stderr,"\
929 \n\nHmmmm.... this sure looks like an ASCII input file to me.\n\
930 The first %d characters are all printable ASCII characters. All of the\n\
931 packet grabbing formats that I understand output BINARY files that I\n\
932 like to read.  Could it be that you've tried to give me the readable \n\
933 output instead?  For example, with tcpdump, you need to use:\n\
934 \t tcpdump -w outfile.dmp ; tcptrace outfile.dmp\n\
935 rather than:\n\
936 \t tcpdump > outfile ; tcptrace outfile\n\n\
937 ", count);
938 		exit(1);
939 	    }
940 	}
941 
942 	exit(1);
943     }
944 
945 #ifndef __WIN32
946     /* open the file for processing */
947     if (CompOpenFile(filename) == NULL) {
948 	exit(-1);
949     }
950 #endif /* __WIN32 */
951 
952     /* how big is it.... (possibly compressed) */
953     if (debug) {
954 	/* print file size */
955 	printf("Trace file size: %lu bytes\n", filesize);
956     }
957     location = 0;
958 
959     /* inform the modules, if they care... */
960     ModulesPerFile(filename);
961 
962     /* count the files */
963     ++file_count;
964 
965 
966     /* read each packet */
967     while (1) {
968         /* read the next packet */
969 	ret = (*ppread)(&current_time,&len,&tlen,&phys,&phystype,&pip,&plast);
970 	if (ret == 0) /* EOF */
971 	    break;
972 
973 	/* update global and per-file packet counters */
974 	++pnum;			/* global */
975 	++fpnum;		/* local to this file */
976 
977 
978 	/* in case only a subset analysis was requested */
979 	if (pnum < beginpnum)	continue;
980 	if ((endpnum != 0) && (pnum > endpnum))	{
981 	    --pnum;
982 	    --fpnum;
983 	    break;
984 	    }
985 
986 
987 	/* check for re-ordered packets */
988 	if (!ZERO_TIME(&last_packet)) {
989 	    if (elapsed(last_packet , current_time) < 0) {
990 		/* out of order */
991 		if ((file_count > 1) && (fpnum == 1)) {
992 		    fprintf(stderr, "\
993 Warning, first packet in file %s comes BEFORE the last packet\n\
994 in the previous file.  That will likely confuse the program, please\n\
995 order the files in time if you have trouble\n", filename);
996 		} else {
997 		    static int warned = 0;
998 
999 		    if (warn_ooo) {
1000 			fprintf(stderr, "\
1001 Warning, packet %ld in file %s comes BEFORE the previous packet\n\
1002 That will likely confuse the program, so be careful!\n",
1003 				fpnum, filename);
1004 		    } else if (!warned) {
1005 			fprintf(stderr, "\
1006 Packets in file %s are out of order.\n\
1007 That will likely confuse the program, so be careful!\n", filename);
1008 		    }
1009 		    warned = 1;
1010 		}
1011 
1012 	    }
1013 	}
1014 
1015 
1016 	/* install signal handler */
1017 	if (fpnum == 1) {
1018 	    signal(SIGINT,QuitSig);
1019 	}
1020 
1021 
1022 	/* progress counters */
1023 	if (!printem && !printallofem && printticks) {
1024 	    if (CompIsCompressed())
1025 		location += tlen;  /* just guess... */
1026 	    if (((fpnum <    100) && (fpnum %    10 == 0)) ||
1027 		((fpnum <   1000) && (fpnum %   100 == 0)) ||
1028 		((fpnum <  10000) && (fpnum %  1000 == 0)) ||
1029 		((fpnum >= 10000) && (fpnum % 10000 == 0))) {
1030 
1031 		unsigned frac;
1032 
1033 		if (debug)
1034 		    fprintf(stderr, "%s: ", cur_filename);
1035 		if (is_stdin) {
1036 		    fprintf(stderr ,"%lu", fpnum);
1037 		} else if (CompIsCompressed()) {
1038 		    frac = location/(filesize/100);
1039 		    if (frac <= 100)
1040 			fprintf(stderr ,"%lu ~%u%% (compressed)", fpnum, frac);
1041 		    else
1042 			fprintf(stderr ,"%lu ~100%% + %u%% (compressed)", fpnum, frac-100);
1043 		} else {
1044 		    location = ftell(stdin);
1045 		    frac = location/(filesize/100);
1046 
1047 		    fprintf(stderr ,"%lu %u%%", fpnum, frac);
1048 		}
1049 		/* print elapsed time */
1050 		{
1051 		    double etime = elapsed(first_packet,last_packet);
1052 		    fprintf(stderr," (%s)", elapsed2str(etime));
1053 		}
1054 
1055 		/* carriage return (but not newline) */
1056 		fprintf(stderr ,"\r");
1057 	    }
1058 	    fflush(stderr);
1059 	}
1060 
1061 
1062 	/* quick sanity check, better be an IPv4/v6 packet */
1063 	if (!PIP_ISV4(pip) && !PIP_ISV6(pip)) {
1064 	    static Bool warned = FALSE;
1065 
1066 	    if (!warned) {
1067 		fprintf(stderr,
1068 			"Warning: saw at least one non-ip packet\n");
1069 		warned = TRUE;
1070 	    }
1071 
1072 	    if (debug)
1073 		fprintf(stderr,
1074 			"Skipping packet %lu, not an IPv4/v6 packet (version:%d)\n",
1075 			pnum,IP_V(pip));
1076 	    continue;
1077 	}
1078 
1079 	/* another sanity check, only understand ETHERNET right now */
1080 	if (phystype != PHYS_ETHER) {
1081 	    static int not_ether = 0;
1082 
1083 	    ++not_ether;
1084 	    if (not_ether == 5) {
1085 		fprintf(stderr,
1086 			"More non-ethernet packets skipped (last warning)\n");
1087 		fprintf(stderr, "\n\
1088 If you'll send me a trace and offer to help, I can add support\n\
1089 for other packet types, I just don't have a place to test them\n\n");
1090 	    } else if (not_ether < 5) {
1091 		fprintf(stderr,
1092 			"Skipping packet %lu, not an ethernet packet\n",
1093 			pnum);
1094 	    } /* else, just shut up */
1095 	    continue;
1096 	}
1097 
1098 	/* print the packet, if requested */
1099 	if (printallofem || dump_packet_data) {
1100 	    printf("Packet %lu\n", pnum);
1101 	    printpacket(len,tlen,phys,phystype,pip,plast,NULL);
1102 	}
1103 
1104 	/* keep track of global times */
1105 	if (ZERO_TIME(&first_packet))
1106 	    first_packet = current_time;
1107 	last_packet = current_time;
1108 
1109 	/* verify IP checksums, if requested */
1110 	if (verify_checksums) {
1111 	    if (!ip_cksum_valid(pip,plast)) {
1112 		++bad_ip_checksums;
1113 		if (warn_printbadcsum)
1114 		    fprintf(stderr, "packet %lu: bad IP checksum\n", pnum);
1115 		continue;
1116 	    }
1117 	}
1118 
1119 	/* find the start of the TCP header */
1120 	ret = gettcp (pip, &ptcp, &plast);
1121 
1122 	/* if that failed, it's not TCP */
1123 	if (ret < 0) {
1124 	    udp_pair *pup;
1125 	    struct udphdr *pudp;
1126 
1127 	    /* look for a UDP header */
1128 	    ret = getudp(pip, &pudp, &plast);
1129 
1130 	    if (do_udp && (ret == 0)) {
1131 		pup = udpdotrace(pip,pudp,plast);
1132 
1133 		/* verify UDP checksums, if requested */
1134 		if (verify_checksums) {
1135 		    if (!udp_cksum_valid(pip,pudp,plast)) {
1136 			++bad_udp_checksums;
1137 			if (warn_printbadcsum)
1138 			    fprintf(stderr, "packet %lu: bad UDP checksum\n",
1139 				    pnum);
1140 			continue;
1141 		    }
1142 		}
1143 
1144 		/* if it's a new connection, tell the modules */
1145 		if (pup && pup->packets == 1)
1146 		    ModulesPerUDPConn(pup);
1147 		/* also, pass the packet to any modules defined */
1148 		ModulesPerUDPPacket(pip,pup,plast);
1149 	    } else if (ret < 0) {
1150 		/* neither UDP not TCP */
1151 		ModulesPerNonTCPUDP(pip,plast);
1152 	    }
1153 	    continue;
1154 	}
1155         else if (ret > 0) { /* not a valid TCP packet */
1156 	  continue;
1157         }
1158 
1159 	/* verify TCP checksums, if requested */
1160 	if (verify_checksums) {
1161 	    if (!tcp_cksum_valid(pip,ptcp,plast)) {
1162 		++bad_tcp_checksums;
1163 		if (warn_printbadcsum)
1164 		    fprintf(stderr, "packet %lu: bad TCP checksum\n", pnum);
1165 		continue;
1166 	    }
1167 	}
1168 
1169         /* perform TCP packet analysis */
1170 	ptp = dotrace(pip,ptcp,plast);
1171 
1172 	/* if it wasn't "interesting", we return NULL here */
1173 	if (ptp == NULL)
1174 	    continue;
1175 
1176 	/* unless this connection is being ignored, tell the modules */
1177 	/* about it */
1178 	if (!ptp->ignore_pair) {
1179 	    /* if it's a new connection, tell the modules */
1180 	    if (ptp->packets == 1)
1181 		ModulesPerConn(ptp);
1182 
1183 	    /* also, pass the packet to any modules defined */
1184 	    ModulesPerPacket(pip,ptp,plast);
1185 	}
1186 
1187 	/* for efficiency, only allow a signal every 1000 packets	*/
1188 	/* (otherwise the system call overhead will kill us)		*/
1189 	if (pnum % 1000 == 0) {
1190 	    sigset_t mask;
1191 
1192 	    sigemptyset(&mask);
1193 	    sigaddset(&mask,SIGINT);
1194 
1195 	    sigprocmask(SIG_UNBLOCK, &mask, NULL);
1196 	    /* signal can happen EXACTLY HERE, when data structures are consistant */
1197 	    sigprocmask(SIG_BLOCK, &mask, NULL);
1198 	}
1199     }
1200 
1201     /* set ^C back to the default */
1202     /* (so we can kill the output if needed) */
1203     {
1204 	sigset_t mask;
1205 
1206 	sigemptyset(&mask);
1207 	sigaddset(&mask,SIGINT);
1208 
1209 	sigprocmask(SIG_UNBLOCK, &mask, NULL);
1210 	signal(SIGINT,SIG_DFL);
1211     }
1212 
1213     /* close the input file */
1214     CompCloseFile(filename);
1215 }
1216 
1217 
1218 static void
QuitSig(int signum)1219 QuitSig(
1220     int signum)
1221 {
1222     printf("%c\n\n", 7);  /* BELL */
1223     printf("Terminating processing early on signal %d\n", signum);
1224     printf("Partial result after processing %lu packets:\n\n\n", pnum);
1225     FinishModules();
1226     plotter_done();
1227     trace_done();
1228     udptrace_done();
1229     exit(1);
1230 }
1231 
1232 
1233 void *
MallocZ(int nbytes)1234 MallocZ(
1235   int nbytes)
1236 {
1237 	char *ptr;
1238 
1239 	ptr = malloc(nbytes);
1240 	if (ptr == NULL) {
1241 		perror("Malloc failed, fatal\n");
1242 		fprintf(stderr,"\
1243 when memory allocation fails, it's either because:\n\
1244 1) You're out of swap space, talk to your local sysadmin about making more\n\
1245    (look for system commands 'swap' or 'swapon' for quick fixes)\n\
1246 2) The amount of memory that your OS gives each process is too little\n\
1247    That's a system configuration issue that you'll need to discuss\n\
1248    with the system administrator\n\
1249 ");
1250 		exit(2);
1251 	}
1252 
1253 	memset(ptr,'\00',nbytes);  /* BZERO */
1254 
1255 	return(ptr);
1256 }
1257 
1258 void *
ReallocZ(void * oldptr,int obytes,int nbytes)1259 ReallocZ(
1260     void *oldptr,
1261     int obytes,
1262     int nbytes)
1263 {
1264 	char *ptr;
1265 
1266 	ptr = realloc(oldptr,nbytes);
1267 	if (ptr == NULL) {
1268 		fprintf(stderr,
1269 			"Realloc failed (%d bytes --> %d bytes), fatal\n",
1270 			obytes, nbytes);
1271 		perror("realloc");
1272 		exit(2);
1273 	}
1274 	if (obytes < nbytes) {
1275 	    memset((char *)ptr+obytes,'\00',nbytes-obytes);  /* BZERO */
1276 	}
1277 
1278 	return(ptr);
1279 }
1280 
1281 static void
Ignore(char * argsource,char * opt)1282 Ignore(
1283        char *argsource,
1284        char *opt)
1285 {
1286      char *o_arg;
1287 
1288      /* next part of arg is a filename or number list */
1289      if (*opt == '\00') {
1290 	  BadArg(argsource,
1291 		 "Expected filename or number list *immediately* after -i / --iTCP\n");
1292      }
1293 
1294      if (run_continuously) {
1295 	  fprintf(stderr,
1296 		  "Warning: cannot ignore connections in continuous mode\n");
1297      }
1298 
1299      /* option is a list of connection numbers separated by commas */
1300      /* option can be immediately "here" or given as a file name */
1301      if (isdigit((int)(*opt)))  // --iTCP1 case
1302 	  o_arg=opt;
1303      else {
1304 	  /* it's in a file */
1305 	  /* open the file */
1306 	  o_arg = FileToBuf(opt);
1307 	  /* if that fails, it's a command line error */
1308 	  if (o_arg == NULL)
1309 	       BadArg(argsource,
1310 		      "Expected filename or number list *immediately* after -i/--iTCP\n");
1311      }
1312      /* wherever we got it, o_arg is a connection list */
1313      while (o_arg && *o_arg) {
1314 	  int num1,num2;
1315 
1316 	  if (sscanf(o_arg,"%d-%d",&num1,&num2) == 2) {
1317 	       /* process range */
1318 	       if (num2 <= num1) {
1319 		    BadArg(argsource,
1320 			   "-iX-Y / --iTCPX-Y, must have X<Y, '%s'\n", o_arg);
1321 	       }
1322 	       if (debug)
1323 		    printf("setting IgnoreConn(%d-%d)\n", num1, num2);
1324 
1325 	       while (num1<=num2) {
1326 		    if (debug > 1)
1327 			 printf("setting IgnoreConn(%d)\n", num1);
1328 		    IgnoreConn(num1++);
1329 
1330 	       }
1331 	  } else if (sscanf(o_arg,"%d",&num1) == 1) {
1332 	       /* single argument */
1333 	       if (debug)
1334 		    printf("setting IgnoreConn(%d)\n", num1);
1335 	       IgnoreConn(num1);
1336 	  } else {
1337 	       /* error */
1338 	       BadArg(argsource,
1339 		      "Don't understand conn number starting at '%s'\n", o_arg);
1340 	  }
1341 
1342 	  /* look for the next comma */
1343 	  o_arg = strchr(o_arg,',');
1344 	  if (o_arg)
1345 	       ++o_arg;
1346      }
1347 }
1348 
1349 static void
GrabOnly(char * argsource,char * opt)1350 GrabOnly(
1351     char *argsource,
1352     char *opt)
1353 {
1354      char *o_arg;
1355 
1356      /* next part of arg is a filename or number list */
1357      if (*opt == '\00') {
1358 	  BadArg(argsource,
1359 		 "Expected filename or number list *immediately* after -o / --oTCP\n");
1360      }
1361 
1362      if (run_continuously) {
1363 	  fprintf(stderr,
1364 		  "Warning: cannot 'grab-only' connections in continuous mode\n");
1365      }
1366 
1367      /* option is a list of connection numbers separated by commas */
1368      /* option can be immediately "here" or given as a file name */
1369      if (isdigit((int)(*opt))) {
1370 	  /* list is on the command line */
1371 	  o_arg = opt;
1372      } else {
1373 	  /* it's in a file */
1374 	  /* open the file */
1375 	  o_arg = FileToBuf(opt);
1376 
1377 	  /* if that fails, it's a command line error */
1378 	  if (o_arg == NULL) {
1379 	       BadArg(argsource,"Expected filename or number list *immediately* after -o / --oTCP\n");
1380 	  }
1381      }
1382 
1383      /* wherever we got it, o_arg is a connection list */
1384      while (o_arg && *o_arg) {
1385 	  int num1,num2;
1386 
1387 	  if (sscanf(o_arg,"%d-%d",&num1,&num2) == 2) {
1388 	       /* process range */
1389 	       if (num2 <= num1) {
1390 		    BadArg(argsource,
1391 			   "-oX-Y / --oTCPX-Y, must have X<Y, '%s'\n",
1392 			   o_arg);
1393 	       }
1394 	       if (debug)
1395 		    printf("setting OnlyConn(%d-%d)\n", num1, num2);
1396 
1397 	       while (num1<=num2) {
1398 		    if (debug > 1)
1399 			 printf("setting OnlyConn(%d)\n", num1);
1400 		    OnlyConn(num1++);
1401 	       }
1402 	  } else if (sscanf(o_arg,"%d",&num1) == 1) {
1403 	       /* single argument */
1404 	       if (debug)
1405 		    printf("setting OnlyConn(%d)\n", num1);
1406 	       OnlyConn(num1);
1407 	  } else {
1408 	       /* error */
1409 	       BadArg(argsource,
1410 		      "Don't understand conn number starting at '%s'\n", o_arg);
1411 	  }
1412 
1413 	  /* look for the next comma */
1414 	  o_arg = strchr(o_arg,',');
1415 	  if (o_arg)
1416 	       ++o_arg;
1417      }
1418 }
1419 
1420 static void
IgnoreUDP(char * argsource,char * opt)1421      IgnoreUDP(
1422 	       char *argsource,
1423 	       char *opt)
1424 {
1425      char *o_arg;
1426 
1427      /* next part of arg is a filename or number list */
1428      if (*opt == '\00') {
1429 	  BadArg(argsource,
1430 		 "Expected filename or number list *immediately* after --iUDP\n");
1431      }
1432 
1433      if (run_continuously) {
1434 	  fprintf(stderr,
1435 		  "Warning: cannot ignore UDP connections in continuous mode\n");
1436      }
1437 
1438      /* option is a list of connection numbers separated by commas */
1439      /* option can be immediately "here" or given as a file name */
1440      if (isdigit((int)(*opt)))  // --iUDP1 case
1441 	  o_arg=opt;
1442      else {
1443 	  /* it's in a file */
1444 	  /* open the file */
1445 	  o_arg = FileToBuf(opt);
1446 	  /* if that fails, it's a command line error */
1447 	  if (o_arg == NULL)
1448 	       BadArg(argsource,
1449 		      "Expected filename or number list *immediately* after --iUDP\n");
1450 
1451      }
1452      /* wherever we got it, o_arg is a connection list */
1453      while (o_arg && *o_arg) {
1454 	  int num1,num2;
1455 
1456 	  if (sscanf(o_arg,"%d-%d",&num1,&num2) == 2) {
1457 	       /* process range */
1458 	       if (num2 <= num1) {
1459 		    BadArg(argsource,
1460 			   "--iUDPX-Y, must have X<Y, '%s'\n", o_arg);
1461 	       }
1462 	       if (debug)
1463 		    printf("setting IgnoreUDPConn(%d-%d)\n", num1,num2);
1464 
1465 	       while (num1<=num2) {
1466 		    if (debug > 1)
1467 			 printf("setting IgnoreUDPConn(%d)\n", num1);
1468 		    IgnoreUDPConn(num1++);
1469 
1470 	       }
1471 	  } else if (sscanf(o_arg,"%d",&num1) == 1) {
1472 	       /* single argument */
1473 	       if (debug)
1474 		    printf("setting IgnoreUDPConn(%d)\n", num1);
1475 	       IgnoreUDPConn(num1);
1476 	  } else {
1477 	       /* error */
1478 	       BadArg(argsource,
1479 		      "Don't understand conn number starting at '%s'\n", o_arg);
1480 	  }
1481 
1482 	  /* look for the next comma */
1483 	  o_arg = strchr(o_arg,',');
1484 	  if (o_arg)
1485 	       ++o_arg;
1486      }
1487 }
1488 
1489 static void
GrabOnlyUDP(char * argsource,char * opt)1490      GrabOnlyUDP(
1491 		 char *argsource,
1492 		 char *opt)
1493 {
1494      char *o_arg;
1495 
1496      /* next part of arg is a filename or number list */
1497      if (*opt == '\00') {
1498 	  BadArg(argsource,"Expected filename or number list *immediately* after --oUDP\n");
1499      }
1500 
1501      if (run_continuously) {
1502 	  fprintf(stderr,
1503 		  "Warning: cannot 'grab-only' UDP connections in continuous mode\n");
1504      }
1505 
1506      /* option is a list of connection numbers separated by commas */
1507      /* option can be immediately "here" or given as a file name */
1508      if (isdigit((int)(*opt))) {
1509 	  /* list is on the command line */
1510 	  o_arg = opt;
1511      } else {
1512 	  /* it's in a file */
1513 
1514 	  /* open the file */
1515 	  o_arg = FileToBuf(opt);
1516 
1517 	  /* if that fails, it's a command line error */
1518 	  if (o_arg == NULL) {
1519 	       BadArg(argsource,"Expected filename or number list *immediately* after --oUDP\n");
1520 	  }
1521      }
1522 
1523      /* wherever we got it, o_arg is a connection list */
1524      while (o_arg && *o_arg) {
1525 	  int num1,num2;
1526 
1527 	  if (sscanf(o_arg,"%d-%d",&num1,&num2) == 2) {
1528 	       /* process range */
1529 	       if (num2 <= num1) {
1530 		    BadArg(argsource,
1531 			   "--oUDPX-Y, must have X<Y, '%s'\n", o_arg);
1532 	       }
1533 	       if (debug)
1534 		    printf("setting OnlyUDPConn(%d-%d)\n", num1, num2);
1535 
1536 	       while (num1<=num2) {
1537 		    if (debug > 1)
1538 			 printf("setting OnlyUDPConn(%d)\n", num1);
1539 		    OnlyUDPConn(num1++);
1540 	       }
1541 	  } else if (sscanf(o_arg,"%d",&num1) == 1) {
1542 	       /* single argument */
1543 	       if (debug)
1544 		    printf("setting OnlyUDPConn(%d)\n", num1);
1545 	       OnlyUDPConn(num1);
1546 	  } else {
1547 	       /* error */
1548 	       BadArg(argsource,
1549 		      "Don't understand conn number starting at '%s'\n",
1550 		      o_arg);
1551 	  }
1552 
1553 	  /* look for the next comma */
1554 	  o_arg = strchr(o_arg,',');
1555 	  if (o_arg)
1556 	       ++o_arg;
1557      }
1558 }
1559 
1560 /* convert a buffer to an argc,argv[] pair */
1561 void
StringToArgv(char * buf,int * pargc,char *** pargv)1562 StringToArgv(
1563     char *buf,
1564     int *pargc,
1565     char ***pargv)
1566 {
1567     char **argv;
1568     int nargs = 0;
1569 
1570     /* discard the original string, use a copy */
1571     buf = strdup(buf);
1572 
1573     /* (very pessimistically) make the argv array */
1574     argv = malloc(sizeof(char *) * ((strlen(buf)/2)+1));
1575 
1576     /* skip leading blanks */
1577     while ((*buf != '\00') && (isspace((int)*buf))) {
1578 	if (debug > 10)
1579 	    printf("skipping isspace('%c')\n", *buf);
1580 	++buf;
1581     }
1582 
1583     /* break into args */
1584     for (nargs = 1; *buf != '\00'; ++nargs) {
1585 	char *stringend;
1586 	argv[nargs] = buf;
1587 
1588 	/* search for separator */
1589 	while ((*buf != '\00') && (!isspace((int)*buf))) {
1590 	    if (debug > 10)
1591 		printf("'%c' (%d) is NOT a space\n", *buf, (int)*buf);
1592 	    ++buf;
1593 	}
1594 	stringend = buf;
1595 
1596 	/* skip spaces */
1597 	while ((*buf != '\00') && (isspace((int)*buf))) {
1598 	    if (debug > 10)
1599 		printf("'%c' (%d) IS a space\n", *buf, (int)*buf);
1600 	    ++buf;
1601 	}
1602 
1603 	*stringend = '\00';  /* terminate the previous string */
1604 
1605 	if (debug)
1606 	    printf("  argv[%d] = '%s'\n", nargs, argv[nargs]);
1607     }
1608 
1609     *pargc = nargs;
1610     *pargv = argv;
1611 }
1612 
1613 
1614 static void
CheckArguments(int * pargc,char * argv[])1615 CheckArguments(
1616     int *pargc,
1617     char *argv[])
1618 {
1619     char *home;
1620     char *envariable;
1621     char *rc_path = NULL;
1622     char *rc_buf = NULL;
1623 
1624     /* remember the name of the program for errors... */
1625     progname = argv[0];
1626 
1627     /* first, we read from the config file, "~/.tcptracerc" */
1628     if ((home = getenv("HOME")) != NULL) {
1629 	struct stat statbuf;
1630 
1631 	int rc_len=strlen(home)+strlen(TCPTRACE_RC_FILE)+2;
1632 
1633 	rc_path = malloc(rc_len);
1634 
1635 	snprintf(rc_path,rc_len, "%s/%s", home, TCPTRACE_RC_FILE);
1636 	if (debug>1)
1637 	    printf("Looking for resource file '%s'\n", rc_path);
1638 
1639 	if (stat(rc_path,&statbuf) != 0) {
1640 	    rc_path = NULL;
1641 	} else {
1642 	    int argc;
1643 	    char **argv;
1644 	    char *pch_file;
1645 	    char *pch_new;
1646 	    char *file_buf;
1647 
1648 	    if (debug>1)
1649 		printf("resource file %s exists\n", rc_path);
1650 
1651 	    /* read the file into a buffer */
1652 	    rc_buf = file_buf = FileToBuf(rc_path);
1653 
1654 	    /* if it exists but can't be read, that's a fatal error */
1655 	    if (rc_buf == NULL) {
1656 		fprintf(stderr,
1657 			"Couldn't read resource file '%s'\n", rc_path);
1658 		fprintf(stderr,
1659 			"(either make the file readable or change its name)\n");
1660 		exit(-1);
1661 	    }
1662 
1663 
1664 	    /* make a new buffer to hold the converted string */
1665 	    pch_file = rc_buf;
1666 	    rc_buf = pch_new = MallocZ(strlen(file_buf)+3);
1667 
1668 	    /* loop until end of string */
1669 	    while (*pch_file) {
1670 		if (*pch_file == '\n') {
1671 		    /* turn newlines into spaces */
1672 		    *pch_new++ = ' ';
1673 		    ++pch_file;
1674 		} else if (*pch_file == '#') {
1675 		    /* skip over the '#' */
1676 		    ++pch_file;
1677 
1678 		    /* remove comments (until NULL or newline) */
1679 		    while ((*pch_file != '\00') &&
1680 			   (*pch_file != '\n')) {
1681 			++pch_file;
1682 		    }
1683 		    /* insert a space */
1684 		    *pch_new++ = ' ';
1685 		} else {
1686 		    /* just copy the characters */
1687 		    *pch_new++ = *pch_file++;
1688 		}
1689 	    }
1690 
1691 	    /* append a NULL to pch_new */
1692 	    *pch_new = '\00';
1693 
1694 	    if (debug>2)
1695 		printf("Resource file string: '%s'\n", rc_buf);
1696 
1697 	    /* we're finished with the original buffer, but need to keep pch_new */
1698 	    free(file_buf);
1699 
1700 	    /* parse those args */
1701 	    StringToArgv(rc_buf,&argc,&argv);
1702 	    ParseArgs(TCPTRACE_RC_FILE, &argc, argv);
1703 	}
1704     }
1705 
1706     /* next, we read from the environment variable "TCPTRACEOPTS" */
1707     if ((envariable = getenv(TCPTRACE_ENVARIABLE)) != NULL) {
1708 	int argc;
1709 	char **argv;
1710 
1711 	if (debug)
1712 	    printf("envariable %s contains:\n\t'%s'\n",
1713 		   TCPTRACE_ENVARIABLE, envariable);
1714 
1715 	StringToArgv(envariable,&argc,&argv);
1716 	ParseArgs(TCPTRACE_ENVARIABLE, &argc, argv);
1717     }
1718 
1719     /* lastly, we read the command line arguments */
1720     ParseArgs("command line",pargc,argv);
1721 
1722     /* make sure we found the files */
1723     if (filenames == NULL) {
1724 	BadArg(NULL,"must specify at least one file name\n");
1725     }
1726 
1727     /* if debugging is on, tell what was in the ENV and rc file */
1728     if (debug) {
1729 	if (rc_path)
1730 	    printf("Flags from %s: '%s'\n", rc_path, rc_buf);
1731 	if (envariable)
1732 	    printf("envariable %s contains: '%s'\n",
1733 		   TCPTRACE_ENVARIABLE, envariable);
1734     }
1735 
1736     if (rc_buf)
1737 	free(rc_buf);
1738 
1739     /* heuristic, I set "-t" in my config file, but they don't work inside */
1740     /* emacs shell windows, which is a pain.  If the terminal looks like EMACS, */
1741     /* then turn OFF ticks! */
1742     if (printticks) {
1743 	char *TERM = getenv("TERM");
1744 	/* allow emacs and Emacs */
1745 	if ((TERM != NULL) &&
1746 	    ((strstr(TERM,"emacs") != NULL) ||
1747 	     (strstr(TERM,"Emacs") != NULL))) {
1748 	    printf("Disabling ticks for EMACS shell window\n");
1749 	    printticks = 0;
1750 	}
1751     }
1752 }
1753 
1754 // these extended options are table driven, to make it easier to
1755 // add more later without messing them up.
1756 // Initially they include --iTCP, --iUDP to ignore TCP, UDP connections
1757 // and --oTCP, --oUDP to output only TCP, UDP connections.
1758 static int
ParseExtendedOpt(char * argsource,char * arg)1759 ParseExtendedOpt(
1760     char *argsource,
1761     char *arg)
1762 {
1763      int i;
1764      struct ext_opt *popt_found = NULL;
1765      char *argtext,*opt=NULL;
1766      int arglen;
1767 
1768      /* there must be at least SOME text there */
1769      if (strcmp(arg,"--") == 0)
1770 	  BadArg(argsource, "Void extended filter argument\n");
1771 
1772      /* find just the arg text */
1773      argtext = arg+2;
1774      arglen = strlen(argtext);
1775 
1776 
1777      /* search for a match on each extended boolean opt */
1778      for (i=0; i < NUM_EXTENDED_OPTIONS; ++i) {
1779 	  struct ext_opt *popt = &extended_options[i];
1780 
1781 	  if (strncasecmp(argtext,popt->opt_name,
1782 		      strlen(popt->opt_name)) == 0 ) {
1783 	       popt_found = popt;
1784 	       opt=argtext+strlen(popt->opt_name);
1785 	       break;
1786 	  }
1787      }
1788 
1789      /* if we never found a match, it's an error */
1790      if (popt_found == NULL)
1791 	  return 0;
1792 
1793      (*popt_found->opt_func)(argsource,opt);
1794      return 1;
1795 }
1796 
1797 
1798 /* these extended boolean options are table driven, to make it easier to
1799    add more later without messing them up */
1800 static void
ParseExtendedBool(char * argsource,char * arg)1801 ParseExtendedBool(
1802     char *argsource,
1803     char *arg)
1804 {
1805     int i;
1806     struct ext_bool_op *pbop_found = NULL;
1807     struct ext_bool_op *pbop_prefix = NULL;
1808     Bool prefix_ambig = FALSE;
1809     Bool negative_arg_prefix;
1810     char *argtext;
1811     int arglen;
1812 
1813     /* there must be at least SOME text there */
1814     if ((strcmp(arg,"--") == 0) || (strcmp(arg,"--no") == 0))
1815 	BadArg(argsource, "Void extended boolean argument\n");
1816 
1817     /* find just the arg text */
1818     if (strncmp(arg,"--no",4) == 0) {
1819 	argtext = arg+4;
1820 	negative_arg_prefix = TRUE;
1821     } else {
1822 	argtext = arg+2;
1823 	negative_arg_prefix = FALSE;
1824     }
1825     arglen = strlen(argtext);
1826 
1827 
1828     /* search for a match on each extended boolean arg */
1829     for (i=0; i < NUM_EXTENDED_BOOLS; ++i) {
1830 	struct ext_bool_op *pbop = &extended_bools[i];
1831 
1832 	/* check for the default value flag */
1833 	if (strcmp(argtext,pbop->bool_optname) == 0) {
1834 	    pbop_found = pbop;
1835 	    break;
1836 	}
1837 
1838 	/* check for a prefix match */
1839 	if (strncmp(argtext,pbop->bool_optname,arglen) == 0) {
1840 	    if (pbop_prefix == NULL)
1841 		pbop_prefix = pbop;
1842 	    else
1843 		prefix_ambig = TRUE;
1844 	}
1845     }
1846 
1847 
1848     /* if we never found a match, it's an error */
1849     if ((pbop_found == NULL) && (pbop_prefix == NULL))
1850 	BadArg(argsource, "Unknown extended boolean argument '%s' (see -hargs)\n", arg);
1851 
1852 
1853     /* if the prefix is UNambiguous, that's good enough */
1854     if ((pbop_prefix != NULL) && (!prefix_ambig))
1855 	pbop_found = pbop_prefix;
1856 
1857     /* either exact match or good prefix, do it */
1858     if (pbop_found != NULL) {
1859 	if (negative_arg_prefix)
1860 	    *pbop_found->bool_popt = !pbop_found->bool_default;
1861 	else
1862 	    *pbop_found->bool_popt = pbop_found->bool_default;
1863 	if (debug>2)
1864 	    fprintf(stderr,"Set boolean variable '%s' to '%s'\n",
1865 		    argtext, BOOL2STR(*pbop_found->bool_popt));
1866 	return;
1867     }
1868 
1869     /* ... else ambiguous prefix */
1870     fprintf(stderr,"Extended boolean arg '%s' is ambiguous, it matches:\n", arg);
1871     for (i=0; i < NUM_EXTENDED_BOOLS; ++i) {
1872 	struct ext_bool_op *pbop = &extended_bools[i];
1873 	if (strncmp(argtext,pbop->bool_optname,arglen) == 0)
1874 	    fprintf(stderr,"  %s%s - %s%s\n",
1875 		    negative_arg_prefix?"no":"",
1876 		    pbop->bool_optname,
1877 		    negative_arg_prefix?"DON'T ":"",
1878 		    pbop->bool_descr);
1879     }
1880     BadArg(argsource, "Ambiguous extended argument '%s'\n", arg);
1881 
1882     return;
1883 }
1884 
1885 
1886 
1887 /* these extended variable options are table driven, to make it easier to add more
1888    later without messing them up */
1889 /* note: the format is of the form   --output_dir=string   */
1890 /* note2: if the string was quoted as --output_dir="this directory"
1891    then those quotes were removed by the shell */
1892 static void
ParseExtendedVar(char * argsource,char * arg_in)1893 ParseExtendedVar(
1894     char *argsource,
1895     char *arg_in)
1896 {
1897     int i;
1898     struct ext_var_op *pvop_found = NULL;
1899     struct ext_var_op *pvop_prefix = NULL;
1900     Bool prefix_ambig = FALSE;
1901     char *pequals;
1902     char *argname;		/* the variable name itself */
1903     char *argval;		/* the part just beyond the equal sign */
1904     int arglen;
1905     char *arg;
1906 
1907     /* we're going to modify the argument to split it in half, we we'd
1908        better make a copy first */
1909     /* note that the only way out of this routine is through BadArg(),
1910        which just exits, or the single return() below, so this isn't
1911        a memory leak*/
1912     arg = strdup(arg_in);
1913 
1914     /* there must be at least SOME text there */
1915     if ((strcmp(arg,"--") == 0))
1916 	BadArg(argsource, "Void extended variable argument\n");
1917 
1918     /* find the '=' sign, it MUST be there */
1919     /* (can't really happen, because the '=' forced us to this routine */
1920     pequals=strchr(arg,'=');
1921     if (!pequals)
1922 	BadArg(argsource, "Extended variable argument with no assignment \n");
1923 
1924 
1925     /* break the arg in half at the '=' sign (located above) */
1926     argname = arg+2;
1927     argval = pequals+1;
1928     *pequals = '\00';		/* split the string here */
1929     /* --output_dir=test */
1930     /*   ^ argname = 1002 */
1931     /*              ^ argval = 1013 */
1932     /*  therefore length = argval(1013)-argname(1002)-1 (10) */
1933     arglen = argval - argname - 1;
1934 
1935     /* search for a match in the extended variable table */
1936     for (i=0; i < NUM_EXTENDED_VARS; ++i) {
1937 	struct ext_var_op *pvop = &extended_vars[i];
1938 
1939 	/* check for an exact match */
1940 	if (strcmp(argname,pvop->var_optname) == 0) {
1941 	    pvop_found = pvop;
1942 	    break;
1943 	}
1944 
1945 	/* check for a prefix match */
1946 	if (strncmp(argname,pvop->var_optname,arglen) == 0) {
1947 	    if (pvop_prefix == NULL)
1948 		pvop_prefix = pvop;
1949 	    else
1950 		prefix_ambig = TRUE; /* already found one */
1951 	}
1952     }
1953 
1954 
1955     /* if we never found a match, it's an error */
1956     if ((pvop_found == NULL) && (pvop_prefix == NULL))
1957 	BadArg(argsource, "Unknown extended variable argument '%s' (see -hargs)\n", arg);
1958 
1959 
1960     /* if the prefix is UNambiguous, that's good enough */
1961     if ((pvop_prefix != NULL) && (!prefix_ambig))
1962 	pvop_found = pvop_prefix;
1963 
1964     /* either exact match or good prefix, do it */
1965     if (pvop_found != NULL) {
1966 	*pvop_found->var_popt = strdup(argval);
1967 	if (debug>2)
1968 	    fprintf(stderr,"Set extended variable '%s' to '%s'\n",
1969 		    argname, *pvop_found->var_popt);
1970 	if (pvop_found->var_verify) {
1971 	    /* call the verification routine */
1972 	    if (debug>2)
1973 		fprintf(stderr,"verifying extended variable '%s'\n", argname);
1974 	    (*pvop_found->var_verify)(argname,*pvop_found->var_popt);
1975 	}
1976 	free(arg);
1977 	return;
1978     }
1979 
1980     /* ... else ambiguous prefix */
1981     fprintf(stderr,"Extended variable arg '%s' is ambiguous, it matches:\n", arg);
1982     for (i=0; i < NUM_EXTENDED_VARS; ++i) {
1983 	struct ext_var_op *pvop = &extended_vars[i];
1984 	if (strncmp(argname,pvop->var_optname,arglen) == 0)
1985 	    fprintf(stderr,"  %s - %s\n",
1986 		    pvop->var_optname, pvop->var_descr);
1987     }
1988     BadArg(argsource, "Ambiguous extended variable argument '%s'\n", arg);
1989     /* never returns */
1990 }
1991 
1992 
1993 
1994 static u_long
VerifyPositive(char * varname,char * value)1995 VerifyPositive(
1996     char *varname,
1997     char *value)
1998 {
1999     int i, ivalue = 0;
2000 
2001     for (i = 0; i < strlen(value); i++) {
2002         if (!isdigit((int)value[i])) {
2003 	    fprintf(stderr,
2004 		    "Value '%s' is not valid for variable '%s'\n",
2005 		    value, varname);
2006 	    exit(1);
2007 	}
2008     }
2009     ivalue = atoi(value);
2010     if (ivalue <= 0) {
2011 	fprintf(stderr,
2012 		"Value '%s' is not valid for variable '%s'\n",
2013 		value, varname);
2014 	exit(1);
2015     }
2016 
2017     return (u_long)ivalue;
2018 }
2019 
2020 
2021 static void
VerifyUpdateInt(char * varname,char * value)2022 VerifyUpdateInt(
2023     char *varname,
2024     char *value)
2025 {
2026     update_interval = VerifyPositive(varname, value);
2027 }
2028 
2029 
2030 static void
VerifyMaxConnNum(char * varname,char * value)2031 VerifyMaxConnNum(
2032     char *varname,
2033     char *value)
2034 {
2035     max_conn_num = VerifyPositive(varname, value);
2036     conn_num_threshold = TRUE;
2037 }
2038 
2039 
2040 static void
VerifyLiveConnInt(char * varname,char * value)2041 VerifyLiveConnInt(
2042     char *varname,
2043     char *value)
2044 {
2045     remove_live_conn_interval = VerifyPositive(varname, value);
2046 }
2047 
2048 static void
VerifyNonrealLiveConnInt(char * varname,char * value)2049   VerifyNonrealLiveConnInt(
2050 		        char *varname,
2051 		        char *value)
2052 {
2053    nonreal_live_conn_interval = VerifyPositive(varname, value);
2054 }
2055 
2056 
2057 static void
VerifyClosedConnInt(char * varname,char * value)2058 VerifyClosedConnInt(
2059     char *varname,
2060     char *value)
2061 {
2062     remove_closed_conn_interval = VerifyPositive(varname, value);
2063 }
2064 
2065 
2066 
2067 static void
ParseArgs(char * argsource,int * pargc,char * argv[])2068 ParseArgs(
2069     char *argsource,
2070     int *pargc,
2071     char *argv[])
2072 {
2073     int i;
2074     int saw_i_or_o = 0;
2075 
2076     /* parse the args */
2077     for (i=1; i < *pargc; ++i) {
2078 	/* modules might have stolen args... */
2079 	if (argv[i] == NULL)
2080 	    continue;
2081 
2082 	// Arguments beginning with "--" could be an extended option
2083 	// as in --iUDP2 , --iTCP3-5, --oUDP5-9,19 etc
2084 	// or they could be the regular extended variables or booleans.
2085 	if (strncmp(argv[i],"--",2) == 0) {
2086 	     if (ParseExtendedOpt(argsource,argv[i]))
2087 		  continue;
2088 	     else {
2089 		  if (strchr(argv[i],'=') != NULL)
2090 		       ParseExtendedVar(argsource, argv[i]);
2091 		  else
2092 		       ParseExtendedBool(argsource, argv[i]);
2093 		  continue;
2094 	     }
2095 	}
2096 
2097 	if (*argv[i] == '-') {
2098 	    if (argv[i][1] == '\00') /* just a '-' */
2099 		Usage();
2100 
2101 	    while (*(++argv[i]))
2102 		switch (*argv[i]) {
2103 		  case 'A':
2104 		    if (isdigit((int)(*(argv[i]+1))))
2105 			thru_interval = atoi(argv[i]+1);
2106 		    else
2107 			BadArg(argsource, "-A  number missing\n");
2108 		    if (thru_interval <= 0)
2109 			BadArg(argsource, "-A  must be > 1\n");
2110 		    *(argv[i]+1) = '\00'; break;
2111 		  case 'B':
2112 		    if (isdigit((int)(*(argv[i]+1))))
2113 			beginpnum = atoi(argv[i]+1);
2114 		    else
2115 			BadArg(argsource, "-B  number missing\n");
2116 		    if (beginpnum < 0)
2117 			BadArg(argsource, "-B  must be >= 0\n");
2118 		    *(argv[i]+1) = '\00'; break;
2119 		  case 'C': colorplot = TRUE; break;
2120 		  case 'D': hex = FALSE; break;
2121 		  case 'E':
2122 		    if (isdigit((int)(*(argv[i]+1))))
2123 			endpnum = atoi(argv[i]+1);
2124 		    else
2125 			BadArg(argsource, "-E  number missing\n");
2126 		    if (beginpnum < 0)
2127 			BadArg(argsource, "-E  must be >= 0\n");
2128 		    *(argv[i]+1) = '\00'; break;
2129 		  case 'F': graph_segsize = TRUE; break;
2130 		  case 'G':
2131 		    graph_tput = TRUE;
2132 		    graph_tsg = TRUE;
2133 		    graph_rtt = TRUE;
2134 		    graph_owin = TRUE;
2135 		    graph_segsize = TRUE;
2136 		    graph_tline = TRUE;
2137 		    graph_recvwin = TRUE;
2138 		    break;
2139 		  case 'L': graph_tline = TRUE;
2140 		    fprintf(stderr, "\nWarning: You have chosen the option '-L' to plot Time Line Graphs.\n         This option is yet under development and may not reflect accurate results.\n         Please take a look at the file README.tline_graphs for more details.\n\n");
2141 		    break;
2142 		  case 'M': colorplot = FALSE; break;
2143 		  case 'N': graph_owin = TRUE; break;
2144 		  case 'O':
2145 		    if (*(argv[i]+1)) {
2146 			/* -Ofile */
2147 			output_filename = strdup(argv[i]+1);
2148 			*(argv[i]+1) = '\00';
2149 		    } else {
2150 			/* maybe -O file */
2151 			BadArg(argsource, "-Ofile requires a file name\n");
2152 		    }
2153 		    break;
2154 		  case 'P': printem = TRUE; break;
2155 		  case 'R': graph_rtt = TRUE; break;
2156 		  case 'S': graph_tsg = TRUE; break;
2157 		  case 'T': graph_tput = TRUE; break;
2158 		  case 'W': print_owin = TRUE; break;
2159 		  case 'X': hex = TRUE; break;
2160 		  case 'Z': dump_rtt = TRUE; break;
2161 		  case 'b': printbrief = TRUE; break;
2162 		  case 'c': ignore_non_comp = TRUE; break;
2163 		  case 'd': ++debug; break;
2164 		  case 'e': save_tcp_data = TRUE; break;
2165 		  case 'f':
2166 		    filter_output = TRUE;
2167 		    if (*(argv[i]+1)) {
2168 			/* -fEXPR */
2169 			ParseFilter(argv[i]+1);
2170 			*(argv[i]+1) = '\00';
2171 		    } else {
2172 			/* -f EXPR */
2173 			BadArg(argsource, "-f requires a filter\n");
2174 		    }
2175 		    break;
2176 		  case 'h': Help(argv[i]+1); *(argv[i]+1) = '\00'; break;
2177 		  case 'i': Ignore(argsource,argv[i]+1);
2178 /*			      {
2179 		      int conn = -1;
2180 		      if (run_continuously) {
2181 			fprintf(stderr, "Warning: cannot ignore connections in continuous mode\n");
2182 		      }
2183 		      else
2184 
2185 		      else {
2186 			  if (isdigit((int)(*(argv[i]+1))))
2187 			      conn = atoi(argv[i]+1);
2188 			  else
2189 			      BadArg(argsource, "-i  number missing\n");
2190 		          if (conn < 0)
2191 			      BadArg(argsource, "-i  must be >= 0\n");
2192  		          ++saw_i_or_o;
2193 		          gIgnoreConn(conn);
2194 		      }
2195  }*/		      *(argv[i]+1) = '\00';
2196 		     break;
2197 		  case 'l': printbrief = FALSE; break;
2198 		  case 'm':
2199 		    BadArg(argsource,
2200 			   "-m option is obsolete (no longer necessary)\n");
2201 		    *(argv[i]+1) = '\00'; break;
2202 		  case 'n':
2203 		    resolve_ipaddresses = FALSE;
2204 		    resolve_ports = FALSE;
2205 		    break;
2206 		  case 'o':
2207 		    if (run_continuously) {
2208 		        fprintf(stderr, "Warning: cannot use 'grab only' flag in continuous mode\n");
2209 		    }
2210 		    else {
2211 		        ++saw_i_or_o;
2212 		        GrabOnly(argsource,argv[i]+1);
2213 		    }
2214 		    *(argv[i]+1) = '\00'; break;
2215 		  case 'p': printallofem = TRUE; break;
2216 		  case 'q': printsuppress = TRUE; break;
2217 		  case 'r': print_rtt = TRUE; break;
2218 		  case 's': use_short_names = TRUE; break;
2219 		  case 't': printticks = TRUE; break;
2220 		  case 'u': do_udp = TRUE; break;
2221 		  case 'v': Version(); exit(0); break;
2222 		  case 'w':
2223 		    warn_printtrunc = TRUE;
2224 		    warn_printbadmbz = TRUE;
2225 		    warn_printhwdups = TRUE;
2226 		    warn_printbadcsum = TRUE;
2227 		    warn_printbad_syn_fin_seq = TRUE;
2228 		    warn_ooo = TRUE;
2229 		    break;
2230 		  case 'x':
2231 		    BadArg(argsource,
2232 			   "unknown module option (-x...)\n");
2233 		    break;
2234 		  case 'y': plot_tput_instant = FALSE; break;
2235 		  case 'z':
2236 		    if (strcmp(argv[i],"z") == 0) {
2237 			/* backward compat, just zero the time */
2238 			graph_time_zero = TRUE;
2239 		    } else if (strcasecmp(argv[i],"zx") == 0) {
2240 			graph_time_zero = TRUE;
2241 		    } else if (strcasecmp(argv[i],"zy") == 0) {
2242 			graph_seq_zero = TRUE;
2243 		    } else if ((strcasecmp(argv[i],"zxy") == 0) ||
2244 			       (strcasecmp(argv[i],"zyx") == 0)) {
2245 			/* set BOTH to zero */
2246 			graph_time_zero = TRUE;
2247 			graph_seq_zero = TRUE;
2248 		    } else {
2249 			BadArg(argsource, "only -z -zx -zy and -zxy are legal\n");
2250 		    }
2251 		    *(argv[i]+1) = '\00';
2252 		    break;
2253 		  default:
2254 		    BadArg(argsource,
2255 			   "option '%c' not understood\n", *argv[i]);
2256 		}
2257 	} else if (*argv[i] == '+') {
2258 	    /* a few of them have a REVERSE flag too */
2259 	    if (argv[i][1] == '\00') /* just a '+' */
2260 		Usage();
2261 
2262 	    while (*(++argv[i]))
2263 		switch (*argv[i]) {
2264 		  case 'C': colorplot = !TRUE; break;
2265 		  case 'D': hex = !FALSE; break;
2266 		  case 'F': graph_segsize = !TRUE; break;
2267 		  case 'L': graph_tline = !TRUE; break;
2268 		  case 'M': colorplot = !FALSE; break;
2269 		  case 'N': graph_owin = !TRUE; break;
2270 		  case 'P': printem = !TRUE; break;
2271 		  case 'R': graph_rtt = !TRUE; break;
2272 		  case 'S': graph_tsg = !TRUE; break;
2273 		  case 'T': graph_tput = !TRUE; break;
2274 		  case 'W': print_owin = !TRUE; break;
2275 		  case 'X': hex = !TRUE; break;
2276 		  case 'Z': dump_rtt = !TRUE; break;
2277 		  case 'b': printbrief = !TRUE; break;
2278 		  case 'c': ignore_non_comp = !TRUE; break;
2279 		  case 'e': save_tcp_data = FALSE; break;
2280 		  case 'l': printbrief = !FALSE; break;
2281 		  case 'n':
2282 		    resolve_ipaddresses = !FALSE;
2283 		    resolve_ports = !FALSE;
2284 		    break;
2285 		  case 'p': printallofem = !TRUE; break;
2286 		  case 'q': printsuppress = !TRUE; break;
2287 		  case 'r': print_rtt = !TRUE; break;
2288 		  case 's': use_short_names = !TRUE; break;
2289 		  case 't': printticks = !TRUE; break;
2290 		  case 'u': do_udp = !TRUE; break;
2291 		  case 'w':
2292 		    warn_printtrunc = !TRUE;
2293 		    warn_printbadmbz = !TRUE;
2294 		    warn_printhwdups = !TRUE;
2295 		    warn_printbadcsum = !TRUE;
2296 		    warn_ooo = !TRUE;
2297 		    break;
2298 		  case 'y': plot_tput_instant = !plot_tput_instant; break;
2299 		  case 'z':
2300 		    if (strcmp(argv[i],"z") == 0) {
2301 			/* backward compat, just zero the time */
2302 			graph_time_zero = !TRUE;
2303 		    } else if (strcasecmp(argv[i],"zx") == 0) {
2304 			graph_time_zero = !TRUE;
2305 		    } else if (strcasecmp(argv[i],"zy") == 0) {
2306 			graph_seq_zero = !TRUE;
2307 		    } else if ((strcasecmp(argv[i],"zxy") == 0) ||
2308 			       (strcasecmp(argv[i],"zyx") == 0)) {
2309 			/* set BOTH to zero */
2310 			graph_time_zero = !TRUE;
2311 			graph_seq_zero = !TRUE;
2312 		    } else {
2313 			BadArg(argsource, "only +z +zx +zy and +zxy are legal\n");
2314 		    }
2315 		    *(argv[i]+1) = '\00';
2316 		    break;
2317 		  default:
2318 		    Usage();
2319 		}
2320 	} else {
2321 	    filenames = &argv[i];
2322 	    *pargc -= i;
2323 	    return;
2324 	}
2325     }
2326 
2327     return;
2328 }
2329 
2330 
2331 static void
DumpFlags(void)2332 DumpFlags(void)
2333 {
2334     int i;
2335 
2336     fprintf(stderr,"printbrief:       %s\n", BOOL2STR(printbrief));
2337     fprintf(stderr,"printsuppress:    %s\n", BOOL2STR(printsuppress));
2338     fprintf(stderr,"print_rtt:        %s\n", BOOL2STR(print_rtt));
2339     fprintf(stderr,"graph rtt:        %s\n", BOOL2STR(graph_rtt));
2340     fprintf(stderr,"graph tput:       %s\n", BOOL2STR(graph_tput));
2341     fprintf(stderr,"graph tsg:        %s\n", BOOL2STR(graph_tsg));
2342     fprintf(stderr,"graph segsize:    %s\n", BOOL2STR(graph_segsize));
2343     fprintf(stderr,"graph owin:       %s\n", BOOL2STR(graph_owin));
2344     fprintf(stderr,"graph tline:      %s\n", BOOL2STR(graph_tline));
2345     fprintf(stderr,"graph recvwin:    %s\n", BOOL2STR(graph_recvwin));
2346     fprintf(stderr,"plotem:           %s\n",
2347 	    colorplot?"(color)":"(b/w)");
2348     fprintf(stderr,"hex printing:     %s\n", BOOL2STR(hex));
2349     fprintf(stderr,"ignore_non_comp:  %s\n", BOOL2STR(ignore_non_comp));
2350     fprintf(stderr,"printem:          %s\n", BOOL2STR(printem));
2351     fprintf(stderr,"printallofem:     %s\n", BOOL2STR(printallofem));
2352     fprintf(stderr,"printticks:       %s\n", BOOL2STR(printticks));
2353     fprintf(stderr,"use_short_names:  %s\n", BOOL2STR(use_short_names));
2354     fprintf(stderr,"save_tcp_data:    %s\n", BOOL2STR(save_tcp_data));
2355     fprintf(stderr,"graph_time_zero:  %s\n", BOOL2STR(graph_time_zero));
2356     fprintf(stderr,"graph_seq_zero:   %s\n", BOOL2STR(graph_seq_zero));
2357     fprintf(stderr,"beginning pnum:   %lu\n", beginpnum);
2358     fprintf(stderr,"ending pnum:      %lu\n", endpnum);
2359     fprintf(stderr,"throughput intvl: %d\n", thru_interval);
2360     fprintf(stderr,"NS simulator hdrs:%s\n", BOOL2STR(ns_hdrs));
2361     fprintf(stderr,"number modules:   %u\n", (unsigned)NUM_MODULES);
2362     fprintf(stderr,"debug:            %s\n", BOOL2STR(debug));
2363 
2364     /* print out the stuff controlled by the extended boolean args */
2365     for (i=0; i < NUM_EXTENDED_BOOLS; ++i) {
2366 	struct ext_bool_op *pbop = &extended_bools[i];
2367 	char buf[100];
2368 	snprintf(buf,sizeof(buf),"%s:", pbop->bool_optname);
2369 	fprintf(stderr,"%-18s%s\n", buf, BOOL2STR(*pbop->bool_popt));
2370     }
2371 
2372     /* print out the stuff controlled by the extended variable args */
2373     for (i=0; i < NUM_EXTENDED_VARS; ++i) {
2374 	struct ext_var_op *bvop = &extended_vars[i];
2375 	char buf[100];
2376 	snprintf(buf,sizeof(buf),"%s:", bvop->var_optname);
2377 	fprintf(stderr,"%-18s%s\n", buf,
2378 		(*bvop->var_popt)?*bvop->var_popt:"<NULL>");
2379     }
2380 }
2381 
2382 
2383 static void
LoadModules(int argc,char * argv[])2384 LoadModules(
2385     int argc,
2386     char *argv[])
2387 {
2388     int i;
2389     int enable;
2390 
2391     for (i=0; i < NUM_MODULES; ++i) {
2392 	++num_modules;
2393 	if (debug)
2394 	    fprintf(stderr,"Initializing module \"%s\"\n",
2395 		    modules[i].module_name);
2396 	enable = (*modules[i].module_init)(argc,argv);
2397 	if (enable) {
2398 	    if (debug)
2399 		fprintf(stderr,"Module \"%s\" enabled\n",
2400 			modules[i].module_name);
2401 	    modules[i].module_inuse = TRUE;
2402 	} else {
2403 	    if (debug)
2404 		fprintf(stderr,"Module \"%s\" not active\n",
2405 			modules[i].module_name);
2406 	    modules[i].module_inuse = FALSE;
2407 	}
2408     }
2409 
2410 }
2411 
2412 
2413 
2414 static void
FinishModules(void)2415 FinishModules(void)
2416 {
2417     int i;
2418 
2419     for (i=0; i < NUM_MODULES; ++i) {
2420 	if (!modules[i].module_inuse)
2421 	    continue;  /* might be disabled */
2422 
2423 	if (modules[i].module_done == NULL)
2424 	    continue;  /* might not have a cleanup */
2425 
2426 	if (debug)
2427 	    fprintf(stderr,"Calling cleanup for module \"%s\"\n",
2428 		    modules[i].module_name);
2429 
2430 	(*modules[i].module_done)();
2431     }
2432 }
2433 
2434 
2435 static void
ModulesPerConn(tcp_pair * ptp)2436 ModulesPerConn(
2437     tcp_pair *ptp)
2438 {
2439     int i;
2440     void *pmodstruct;
2441 
2442     for (i=0; i < NUM_MODULES; ++i) {
2443 	if (!modules[i].module_inuse)
2444 	    continue;  /* might be disabled */
2445 
2446 	if (modules[i].module_newconn == NULL)
2447 	    continue;  /* they might not care */
2448 
2449 	if (debug>3)
2450 	    fprintf(stderr,"Calling newconn routine for module \"%s\"\n",
2451 		    modules[i].module_name);
2452 
2453 	pmodstruct = (*modules[i].module_newconn)(ptp);
2454 	if (pmodstruct) {
2455 	    /* make sure the array is there */
2456 	    if (!ptp->pmod_info) {
2457 		ptp->pmod_info = MallocZ(num_modules * sizeof(void *));
2458 	    }
2459 
2460 	    /* remember this structure */
2461 	    ptp->pmod_info[i] = pmodstruct;
2462 	}
2463     }
2464 }
2465 
2466 
2467 void
ModulesPerOldConn(tcp_pair * ptp)2468 ModulesPerOldConn(
2469 		  tcp_pair *ptp)
2470 {
2471     int i;
2472 
2473     for (i=0; i < NUM_MODULES; ++i) {
2474 	if (!modules[i].module_inuse)
2475 	    continue;  /* might be disabled */
2476 
2477 	if (modules[i].module_deleteconn == NULL)
2478 	    continue;  /* they might not care */
2479 
2480 	if (debug>3)
2481 	    fprintf(stderr,"Calling delete conn routine for module \"%s\"\n",
2482 		    modules[i].module_name);
2483 
2484 	(*modules[i].module_deleteconn)(ptp,
2485 					ptp->pmod_info?ptp->pmod_info[i]:NULL);
2486     }
2487 }
2488 
2489 
2490 static void
ModulesPerUDPConn(udp_pair * pup)2491 ModulesPerUDPConn(
2492     udp_pair *pup)
2493 {
2494     int i;
2495     void *pmodstruct;
2496 
2497     for (i=0; i < NUM_MODULES; ++i) {
2498 	if (!modules[i].module_inuse)
2499 	    continue;  /* might be disabled */
2500 
2501 	if (modules[i].module_udp_newconn == NULL)
2502 	    continue;  /* they might not care */
2503 
2504 	if (debug>3)
2505 	    fprintf(stderr,"Calling UDP newconn routine for module \"%s\"\n",
2506 		    modules[i].module_name);
2507 
2508 	pmodstruct = (*modules[i].module_udp_newconn)(pup);
2509 	if (pmodstruct) {
2510 	    /* make sure the array is there */
2511 	    if (!pup->pmod_info) {
2512 		pup->pmod_info = MallocZ(num_modules * sizeof(void *));
2513 	    }
2514 
2515 	    /* remember this structure */
2516 	    pup->pmod_info[i] = pmodstruct;
2517 	}
2518     }
2519 }
2520 
2521 static void
ModulesPerNonTCPUDP(struct ip * pip,void * plast)2522 ModulesPerNonTCPUDP(
2523     struct ip *pip,
2524     void *plast)
2525 {
2526     int i;
2527 
2528     for (i=0; i < NUM_MODULES; ++i) {
2529 	if (!modules[i].module_inuse)
2530 	    continue;  /* might be disabled */
2531 
2532 	if (modules[i].module_nontcpudp_read == NULL)
2533 	    continue;  /* they might not care */
2534 
2535 	if (debug>3)
2536 	    fprintf(stderr,"Calling nontcp routine for module \"%s\"\n",
2537 		    modules[i].module_name);
2538 
2539 	(*modules[i].module_nontcpudp_read)(pip,plast);
2540     }
2541 }
2542 
2543 
2544 static void
ModulesPerPacket(struct ip * pip,tcp_pair * ptp,void * plast)2545 ModulesPerPacket(
2546     struct ip *pip,
2547     tcp_pair *ptp,
2548     void *plast)
2549 {
2550     int i;
2551 
2552     for (i=0; i < NUM_MODULES; ++i) {
2553 	if (!modules[i].module_inuse)
2554 	    continue;  /* might be disabled */
2555 
2556 	if (modules[i].module_read == NULL)
2557 	    continue;  /* they might not care */
2558 
2559 	if (debug>3)
2560 	    fprintf(stderr,"Calling read routine for module \"%s\"\n",
2561 		    modules[i].module_name);
2562 
2563 	(*modules[i].module_read)(pip,ptp,plast,
2564 				  ptp->pmod_info?ptp->pmod_info[i]:NULL);
2565     }
2566 }
2567 
2568 
2569 static void
ModulesPerUDPPacket(struct ip * pip,udp_pair * pup,void * plast)2570 ModulesPerUDPPacket(
2571     struct ip *pip,
2572     udp_pair *pup,
2573     void *plast)
2574 {
2575     int i;
2576 
2577     for (i=0; i < NUM_MODULES; ++i) {
2578 	if (!modules[i].module_inuse)
2579 	    continue;  /* might be disabled */
2580 
2581 	if (modules[i].module_udp_read == NULL)
2582 	    continue;  /* they might not care */
2583 
2584 	if (debug>3)
2585 	    fprintf(stderr,"Calling read routine for module \"%s\"\n",
2586 		    modules[i].module_name);
2587 
2588 	(*modules[i].module_udp_read)(pip,pup,plast,
2589 				      pup->pmod_info?pup->pmod_info[i]:NULL);
2590     }
2591 }
2592 
2593 
2594 static void
ModulesPerFile(char * filename)2595 ModulesPerFile(
2596     char *filename)
2597 {
2598     int i;
2599 
2600     for (i=0; i < NUM_MODULES; ++i) {
2601 	if (!modules[i].module_inuse)
2602 	    continue;  /* might be disabled */
2603 
2604 	if (modules[i].module_newfile == NULL)
2605 	    continue;  /* they might not care */
2606 
2607 	if (debug>3)
2608 	    fprintf(stderr,"Calling newfile routine for module \"%s\"\n",
2609 		    modules[i].module_name);
2610 
2611 	(*modules[i].module_newfile)(filename,filesize,CompIsCompressed());
2612     }
2613 }
2614 
2615 /* the memcpy() function that gcc likes to stuff into the program has alignment
2616    problems, so here's MY version.  It's only used for small stuff, so the
2617    copy should be "cheap", but we can't be too fancy due to alignment boo boos */
2618 void *
MemCpy(void * vp1,void * vp2,size_t n)2619 MemCpy(void *vp1, void *vp2, size_t n)
2620 {
2621     char *p1 = vp1;
2622     char *p2 = vp2;
2623 
2624     while (n-->0)
2625 	*p1++=*p2++;
2626 
2627     return(vp1);
2628 }
2629 
2630 
2631 /* read from a file, store contents into NULL-terminated string */
2632 /* memory returned must be "free"ed to be reclaimed */
2633 static char *
FileToBuf(char * filename)2634 FileToBuf(
2635     char *filename)
2636 {
2637     FILE *f;
2638     struct stat str_stat;
2639     int filesize;
2640     char *buffer;
2641 
2642     /* open the file */
2643     if ((f = fopen(filename,"r")) == NULL) {
2644 	fprintf(stderr,"Open of '%s' failed\n", filename);
2645 	perror(filename);
2646 	return(NULL);
2647     }
2648 
2649 
2650     /* determine the file length */
2651     if (fstat(fileno(f),&str_stat) != 0) {
2652 	perror("fstat");
2653 	exit(1);
2654     }
2655     filesize = str_stat.st_size;
2656 
2657     /* make a big-enough buffer */
2658     buffer = MallocZ(filesize+2);  /* with room to NULL terminate */
2659 
2660 
2661     /* read the file into the buffer */
2662     if (fread(buffer,1,filesize,f) != filesize) {
2663 	perror("fread");
2664 	exit(1);
2665     }
2666 
2667     fclose(f);
2668 
2669     /* put a NULL at the end */
2670     buffer[filesize] = '\00';
2671 
2672     if (debug > 1)
2673 	printf("Read %d characters from resource '%s': '%s'\n",
2674 	       filesize, filename, buffer);
2675 
2676     /* somebody else will "free" it */
2677     return(buffer);
2678 }
2679 
2680 
2681 /* ExpandFormat:
2682    Expand the string in "format" and return the result string
2683 
2684    The return value rotates between one of two static strings
2685    (to avoid malloc overhead), but if you need more than two at
2686    a time, you'll need to make a copy.
2687 
2688    Expansions are performed as follows:
2689 
2690    %f	basename of the current input file
2691    %d	execution date, standard unix output, spaces ==> underscores
2692    %t	execution time & date, standard unix output, spaces ==> underscores
2693    %D	execution date, format "1-14-1963"
2694 */
2695 
2696 char *
ExpandFormat(const char * format)2697 ExpandFormat(const char *format)
2698 {
2699     static struct dstring *pds1 = NULL;
2700     static struct dstring *pds2 = NULL;
2701     static struct dstring *pds = NULL;
2702 
2703     /* init the strings */
2704     if (pds1 == NULL) {
2705 	pds1 = DSNew();
2706 	pds2 = DSNew();
2707     }
2708 
2709     /* alternate between them */
2710     pds = (pds == pds1)?pds2:pds1;
2711 
2712     /* erase the previous contents */
2713     DSErase(pds);
2714 
2715     if (debug>2)
2716 	fprintf(stderr,"Trying to expand string '%s'\n", format);
2717 
2718     while (*format) {
2719 	if (strncmp(format,"%f",2) == 0) {
2720 	    /* basename of current file (after the last slash) */
2721 	    char *filename = cur_filename;
2722 	    char *ptr;
2723 
2724 	    /* find the last '/' in the file */
2725 	    ptr = strrchr(filename,'/');
2726 
2727 	    if (ptr)
2728 		++ptr;		/* the base of the filename is one past the slash */
2729 	    else
2730 		ptr = filename;	/* no directory, just use the file */
2731 
2732 	    DSAppendString(pds,ptr);
2733 	    format += 2;
2734 	} else if (strncmp(format,"%D",2) == 0) {
2735 	    /* current wallclock date (1-14-1963) */
2736 	    time_t now;
2737 	    struct tm *ptm;
2738 	    char buf[32];
2739 
2740 	    /* get the current time, broken apart */
2741 	    time(&now);
2742 	    ptm = localtime((time_t *)&wallclock_start.tv_sec);
2743 
2744 	    snprintf(buf,sizeof(buf),"%d-%d-%d",
2745 		    ptm->tm_mon+1,
2746 		    ptm->tm_mday,
2747 		    1900 + ptm->tm_year);
2748 	    DSAppendString(pds,buf);
2749 	    format += 2;
2750 	} else if ((strncmp(format,"%d",2) == 0) ||
2751 		   (strncmp(format,"%t",2) == 0)) {
2752 	    /* current wallclock date, unix format */
2753 	    time_t now;
2754 	    char *pbuf;
2755 	    char *pch;
2756 
2757 	    /* get the current time in unix format */
2758             /* Fri Sep 13 00:00:00 1986\n\0 */
2759 	    /*           1         2       */
2760 	    /* 0123456789012345678901234 5 */
2761 	    time(&now);
2762 	    pbuf = ctime(&now);
2763 	    pbuf[24] = '\00';	/* nuke the newline */
2764 
2765 	    /* spaces to underscores */
2766 	    for (pch = pbuf; *pch; ++pch)
2767 		if (*pch == ' ')
2768 		    *pch = '_';
2769 
2770 
2771 	    if (strncmp(format,"%d",2) == 0)
2772 		/* the whole thing */
2773 		DSAppendString(pds,pbuf);
2774 	    else {
2775 		/* just the date */
2776 		pbuf[11] = '\00';
2777 		DSAppendString(pds,pbuf);
2778 		DSAppendString(pds,pbuf+20);
2779 	    }
2780 
2781 	    format += 2;
2782 	} else {
2783 	    /* no formatting, just copy one character */
2784 	    DSAppendChar(pds,*format);
2785 	    ++format;
2786 	}
2787     }
2788 
2789     return(DSVal(pds));
2790 }
2791 
2792