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)(¤t_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