1 /*
2
3 ----------------------------------------------------
4 httpry - HTTP logging and information retrieval tool
5 ----------------------------------------------------
6
7 Copyright (c) 2005-2014 Jason Bittel <jason.bittel@gmail.com>
8
9 */
10
11 #include <ctype.h>
12 #include <fcntl.h>
13 #include <grp.h>
14 #include <pcap.h>
15 #include <pwd.h>
16 #include <signal.h>
17 #include <sys/stat.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <sys/socket.h>
25 #include "config.h"
26 #include "error.h"
27 #include "format.h"
28 #include "methods.h"
29 #include "tcp.h"
30 #include "rate.h"
31
32 /* Function declarations */
33 int getopt(int, char * const *, const char *);
34 pcap_t *prepare_capture(char *interface, int promisc, char *filename, char *capfilter);
35 void set_link_offset(int header_type);
36 void open_outfiles();
37 void runas_daemon();
38 void change_user(char *name);
39 void parse_http_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt);
40 int process_ip6_nh(const u_char *pkt, int size_ip, unsigned int caplen, unsigned int offset);
41 char *parse_header_line(char *header_line);
42 int parse_client_request(char *header_line);
43 int parse_server_response(char *header_line);
44 void handle_signal(int sig);
45 void cleanup();
46 void print_stats();
47 void display_banner();
48 void display_usage();
49
50 /* Program flags/options, set by arguments or config file */
51 static unsigned int parse_count = 0;
52 static int daemon_mode = 0;
53 static int eth_skip_bits = 0;
54 static char *use_infile = NULL;
55 static char *interface = NULL;
56 static char *capfilter = NULL;
57 static char *use_outfile = NULL;
58 static int set_promisc = 1;
59 static char *pid_filename = NULL;
60 static char *new_user = NULL;
61 static char *format_str = NULL;
62 static char *methods_str = NULL;
63 static char *use_dumpfile = NULL;
64 static int rate_stats = 0;
65 static int rate_interval = DEFAULT_RATE_INTERVAL;
66 static int rate_threshold = DEFAULT_RATE_THRESHOLD;
67 static int force_flush = 0;
68 int quiet_mode = 0; /* Defined as extern in error.h */
69 int use_syslog = 0; /* Defined as extern in error.h */
70
71 static pcap_t *pcap_hnd = NULL; /* Opened pcap device handle */
72 static char *buf = NULL;
73 static unsigned int num_parsed = 0; /* Count of fully parsed HTTP packets */
74 static time_t start_time = 0; /* Start tick for statistics calculations */
75 static int link_offset = 0;
76 static pcap_dumper_t *dumpfile = NULL;
77 static char default_capfilter[] = DEFAULT_CAPFILTER;
78 static char default_format[] = DEFAULT_FORMAT;
79 static char rate_format[] = RATE_FORMAT;
80 static char default_methods[] = DEFAULT_METHODS;
81
82 /* Find and prepare ethernet device for capturing */
prepare_capture(char * interface,int promisc,char * filename,char * capfilter)83 pcap_t *prepare_capture(char *interface, int promisc, char *filename, char *capfilter) {
84 char errbuf[PCAP_ERRBUF_SIZE];
85 pcap_t *pcap_hnd;
86 char *dev = NULL;
87 bpf_u_int32 net, mask;
88 struct bpf_program filter;
89
90 if (!filename) {
91 /* Starting live capture, so find and open network device */
92 if (!interface) {
93 dev = pcap_lookupdev(errbuf);
94 if (dev == NULL)
95 LOG_DIE("Cannot find a valid capture device: %s", errbuf);
96 } else {
97 dev = interface;
98 }
99
100 if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) net = 0;
101
102 pcap_hnd = pcap_open_live(dev, BUFSIZ, promisc, 1000, errbuf);
103
104 if (pcap_hnd == NULL)
105 LOG_DIE("Cannot open live capture on '%s': %s", dev, errbuf);
106 } else {
107 /* Reading from a saved capture, so open file */
108 pcap_hnd = pcap_open_offline(filename, errbuf);
109
110 if (pcap_hnd == NULL)
111 LOG_DIE("Cannot open saved capture file: %s", errbuf);
112 }
113
114 set_link_offset(pcap_datalink(pcap_hnd));
115
116 /* Compile capture filter and apply to handle */
117 if (pcap_compile(pcap_hnd, &filter, capfilter, 0, net) == -1)
118 LOG_DIE("Cannot compile capture filter '%s': %s", capfilter, pcap_geterr(pcap_hnd));
119
120 if (pcap_setfilter(pcap_hnd, &filter) == -1)
121 LOG_DIE("Cannot apply capture filter: %s", pcap_geterr(pcap_hnd));
122
123 pcap_freecode(&filter);
124
125 if (!filename) LOG_PRINT("Starting capture on %s interface", dev);
126
127 return pcap_hnd;
128 }
129
130 /* Set the proper packet header offset length based on the datalink type */
set_link_offset(int header_type)131 void set_link_offset(int header_type) {
132
133 #ifdef DEBUG
134 ASSERT(header_type >= 0);
135 #endif
136
137 switch (header_type) {
138 case DLT_EN10MB:
139 link_offset = 14;
140 break;
141 #ifdef DLT_IEEE802_11
142 case DLT_IEEE802_11:
143 link_offset = 32;
144 break;
145 #endif
146 #ifdef DLT_LINUX_SLL
147 case DLT_LINUX_SLL:
148 link_offset = 16;
149 break;
150 #endif
151 #ifdef DLT_LOOP
152 case DLT_LOOP:
153 link_offset = 4;
154 break;
155 #endif
156 case DLT_NULL:
157 link_offset = 4;
158 break;
159 case DLT_RAW:
160 link_offset = 0;
161 break;
162 case DLT_PPP:
163 link_offset = 4;
164 break;
165 #ifdef DLT_PPP_SERIAL
166 case DLT_PPP_SERIAL:
167 #endif
168 case DLT_PPP_ETHER:
169 link_offset = 8;
170 break;
171 default:
172 LOG_DIE("Unsupported datalink type: %s", pcap_datalink_val_to_name(header_type));
173 break;
174 }
175
176 return;
177 }
178
179 /* Open any requested output files */
open_outfiles()180 void open_outfiles() {
181 /* Redirect stdout to the specified output file if requested */
182 if (use_outfile) {
183 if (daemon_mode && (use_outfile[0] != '/'))
184 LOG_WARN("Output file path is not absolute and may be inaccessible after daemonizing");
185
186 if (freopen(use_outfile, "a", stdout) == NULL)
187 LOG_DIE("Cannot reopen output stream to '%s'", use_outfile);
188
189 PRINT("Writing output to file: %s", use_outfile);
190
191 printf("# %s version %s\n", PROG_NAME, PROG_VER);
192 print_format_list();
193 }
194
195 /* Open pcap binary capture file if requested */
196 if (use_dumpfile) {
197 if (daemon_mode && (use_dumpfile[0] != '/'))
198 LOG_WARN("Binary capture file path is not absolute and may be inaccessible after daemonizing");
199
200 if ((dumpfile = pcap_dump_open(pcap_hnd, use_dumpfile)) == NULL)
201 LOG_DIE("Cannot open binary dump file '%s'", use_dumpfile);
202 PRINT("Writing binary dump file: %s", use_dumpfile);
203 }
204
205 return;
206 }
207
208 /* Run program as a daemon process */
runas_daemon()209 void runas_daemon() {
210 int child_pid;
211 FILE *pid_file;
212
213 if (getppid() == 1) return; /* We're already a daemon */
214
215 fflush(NULL);
216
217 child_pid = fork();
218 if (child_pid < 0) LOG_DIE("Cannot fork child process");
219 if (child_pid > 0) exit(0); /* Parent bows out */
220
221 /* Configure default output streams */
222 dup2(1,2);
223 close(0);
224 if (freopen(NULL_FILE, "a", stderr) == NULL)
225 LOG_DIE("Cannot reopen stderr to '%s'", NULL_FILE);
226
227 /* Assign new process group for child */
228 if (setsid() == -1)
229 LOG_WARN("Cannot assign new session for child process");
230
231 umask(022); /* Reset file creation mask */
232 if (chdir("/") == -1)
233 LOG_DIE("Cannot change run directory to '/'");
234
235 /* Create PID file */
236 if (pid_filename[0] != '/')
237 LOG_WARN("PID file path is not absolute and may be inaccessible after daemonizing");
238 if ((pid_file = fopen(pid_filename, "w"))) {
239 fprintf(pid_file, "%d", getpid());
240 fclose(pid_file);
241 } else {
242 LOG_WARN("Cannot open PID file '%s'", pid_filename);
243 }
244
245 signal(SIGCHLD, SIG_IGN);
246 signal(SIGTSTP, SIG_IGN);
247 signal(SIGTTOU, SIG_IGN);
248 signal(SIGTTIN, SIG_IGN);
249 signal(SIGTERM, &handle_signal);
250
251 fflush(NULL);
252
253 return;
254 }
255
256 /* Change process owner to specified username */
change_user(char * name)257 void change_user(char *name) {
258 struct passwd *user = NULL;
259
260 #ifdef DEBUG
261 ASSERT(name);
262 #endif
263
264 if ((getuid() != 0) && (geteuid() != 0))
265 LOG_DIE("You must be root to switch users");
266
267 if (!(user = getpwnam(name)))
268 LOG_DIE("User '%s' not found in system", name);
269
270 /* Change ownership of output files before we drop privs */
271 if (use_outfile) {
272 if (chown(use_outfile, user->pw_uid, user->pw_gid) < 0)
273 LOG_WARN("Cannot change ownership of output file '%s'", use_outfile);
274 }
275
276 if (use_dumpfile) {
277 if (chown(use_dumpfile, user->pw_uid, user->pw_gid) < 0)
278 LOG_WARN("Cannot change ownership of dump file '%s'", use_dumpfile);
279 }
280
281 if (initgroups(name, user->pw_gid))
282 LOG_DIE("Cannot initialize the group access list");
283
284 if (setgid(user->pw_gid)) LOG_DIE("Cannot set GID");
285 if (setuid(user->pw_uid)) LOG_DIE("Cannot set UID");
286
287 /* Test to see if we actually made it to the new user */
288 if ((getegid() != user->pw_gid) || (geteuid() != user->pw_uid))
289 LOG_DIE("Cannot change process owner to '%s'", name);
290
291 return;
292 }
293
294 /* Process each packet that passes the capture filter */
parse_http_packet(u_char * args,const struct pcap_pkthdr * header,const u_char * pkt)295 void parse_http_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt) {
296 struct tm *pkt_time;
297 char *header_line, *req_value;
298 char saddr[INET6_ADDRSTRLEN], daddr[INET6_ADDRSTRLEN];
299 char sport[PORTSTRLEN], dport[PORTSTRLEN];
300 char ts[MAX_TIME_LEN];
301 int is_request = 0, is_response = 0;
302 unsigned int eth_type = 0, offset;
303
304 const struct eth_header *eth;
305 const struct ip_header *ip;
306 const struct ip6_header *ip6;
307 const struct tcp_header *tcp;
308 const char *data;
309 int size_ip, size_tcp, size_data, family;
310
311 /* Check the ethernet type and insert a VLAN offset if necessary */
312 eth = (struct eth_header *) pkt;
313 eth_type = ntohs(eth->ether_type);
314 if (eth_type == ETHER_TYPE_VLAN) {
315 offset = link_offset + 4;
316 } else {
317 offset = link_offset;
318 }
319
320 offset += eth_skip_bits;
321
322 /* Position pointers within packet stream and do sanity checks */
323 ip = (struct ip_header *) (pkt + offset);
324 ip6 = (struct ip6_header *) (pkt + offset);
325
326 switch (IP_V(ip)) {
327 case 4: family = AF_INET; break;
328 case 6: family = AF_INET6; break;
329 default: return;
330 }
331
332 if (family == AF_INET) {
333 size_ip = IP_HL(ip) * 4;
334 if (size_ip < 20) return;
335 if (ip->ip_p != IPPROTO_TCP) return;
336 } else { /* AF_INET6 */
337 size_ip = sizeof(struct ip6_header);
338 if (ip6->ip6_nh != IPPROTO_TCP)
339 size_ip = process_ip6_nh(pkt, size_ip, header->caplen, offset);
340 if (size_ip < 40) return;
341 }
342
343 tcp = (struct tcp_header *) (pkt + offset + size_ip);
344 size_tcp = TH_OFF(tcp) * 4;
345 if (size_tcp < 20) return;
346
347 data = (char *) (pkt + offset + size_ip + size_tcp);
348 size_data = (header->caplen - (offset + size_ip + size_tcp));
349 if (size_data <= 0) return;
350
351 /* Check if we appear to have a valid request or response */
352 if (is_request_method(data)) {
353 is_request = 1;
354 } else if (strncmp(data, HTTP_STRING, strlen(HTTP_STRING)) == 0) {
355 is_response = 1;
356 } else {
357 return;
358 }
359
360 /* Copy packet data to editable buffer that was created in main() */
361 if (size_data > BUFSIZ) size_data = BUFSIZ;
362 memcpy(buf, data, size_data);
363 buf[size_data] = '\0';
364
365 /* Parse header line, bail if malformed */
366 if ((header_line = parse_header_line(buf)) == NULL) return;
367
368 if (is_request) {
369 if (parse_client_request(header_line)) return;
370 } else if (is_response) {
371 if (parse_server_response(header_line)) return;
372 }
373
374 /* Iterate through request/entity header fields */
375 while ((header_line = parse_header_line(NULL)) != NULL) {
376 if ((req_value = strchr(header_line, ':')) == NULL) continue;
377 *req_value++ = '\0';
378 while (isspace(*req_value)) req_value++;
379
380 insert_value(header_line, req_value);
381 }
382
383 /* Grab source/destination IP addresses */
384 if (family == AF_INET) {
385 inet_ntop(family, &ip->ip_src, saddr, sizeof(saddr));
386 inet_ntop(family, &ip->ip_dst, daddr, sizeof(daddr));
387 } else { /* AF_INET6 */
388 inet_ntop(family, &ip6->ip_src, saddr, sizeof(saddr));
389 inet_ntop(family, &ip6->ip_dst, daddr, sizeof(daddr));
390 }
391 insert_value("source-ip", saddr);
392 insert_value("dest-ip", daddr);
393
394 /* Grab source/destination ports */
395 snprintf(sport, PORTSTRLEN, "%d", ntohs(tcp->th_sport));
396 snprintf(dport, PORTSTRLEN, "%d", ntohs(tcp->th_dport));
397 insert_value("source-port", sport);
398 insert_value("dest-port", dport);
399
400 /* Extract packet capture time */
401 pkt_time = localtime((time_t *) &header->ts.tv_sec);
402 strftime(ts, MAX_TIME_LEN, "%Y-%m-%d %H:%M:%S", pkt_time);
403 insert_value("timestamp", ts);
404
405 if (rate_stats) {
406 update_host_stats(get_value("host"), header->ts.tv_sec);
407 clear_values();
408 } else {
409 print_format_values();
410 }
411
412 if (dumpfile)
413 pcap_dump((unsigned char *) dumpfile, header, pkt);
414
415 num_parsed++;
416 if (parse_count && (num_parsed >= parse_count))
417 pcap_breakloop(pcap_hnd);
418
419 return;
420 }
421
422 /* Iterate through IPv6 extension headers looking for a TCP header. Returns
423 the total size of the IPv6 header, including all extension headers.
424 Return 0 to abort processing of this packet. */
process_ip6_nh(const u_char * pkt,int size_ip,unsigned int caplen,unsigned int offset)425 int process_ip6_nh(const u_char *pkt, int size_ip, unsigned int caplen, unsigned int offset) {
426 const struct ip6_ext_header *ip6_eh;
427 unsigned int len = caplen - offset;
428 ip6_eh = (struct ip6_ext_header *) (pkt + offset + size_ip);
429
430 while (ip6_eh->ip6_eh_nh != IPPROTO_TCP) {
431 switch (ip6_eh->ip6_eh_nh) {
432 case 0: /* Hop-by-hop options */
433 case 43: /* Routing */
434 case 44: /* Fragment */
435 case 51: /* Authentication Header */
436 case 50: /* Encapsulating Security Payload */
437 case 60: /* Destination Options */
438 size_ip = size_ip + (ip6_eh->ip6_eh_len * 8) + 8;
439 break;
440 case 59: /* No next header */
441 default:
442 return 0;
443 }
444
445 if (size_ip > len) return 0;
446
447 ip6_eh = (struct ip6_ext_header *) (pkt + offset + size_ip);
448 }
449
450 /* Next header is TCP, so increment past the final extension header */
451 size_ip = size_ip + (ip6_eh->ip6_eh_len * 8) + 8;
452
453 return size_ip;
454 }
455
456 /* Tokenize a HTTP header into lines; the first call should pass the string
457 to tokenize, all subsequent calls for the same string should pass NULL */
parse_header_line(char * header_line)458 char *parse_header_line(char *header_line) {
459 static char *pos;
460 char *tmp;
461
462 if (header_line) pos = header_line;
463
464 /* Search for a '\n' line terminator, ignoring a leading
465 '\r' if it exists (per RFC2616 section 19.3) */
466 tmp = strchr(pos, '\n');
467 if (!tmp && header_line) {
468 return header_line;
469 } else if (!tmp) {
470 return NULL;
471 }
472 *tmp = '\0';
473 if (*(tmp - 1) == '\r') *(--tmp) = '\0';
474
475 if (tmp == pos) return NULL; /* Reached the end of the header */
476
477 header_line = pos;
478 /* Increment past the '\0' character(s) inserted above */
479 if (*tmp == '\0') {
480 tmp++;
481 if (*tmp == '\0') tmp++;
482 }
483 pos = tmp;
484
485 return header_line;
486 }
487
488 /* Parse a HTTP client request; bail at first sign of an invalid request */
parse_client_request(char * header_line)489 int parse_client_request(char *header_line) {
490 char *method, *request_uri, *http_version;
491
492 #ifdef DEBUG
493 ASSERT(header_line);
494 ASSERT(strlen(header_line) > 0);
495 #endif
496
497 method = header_line;
498
499 if ((request_uri = strchr(method, ' ')) == NULL) return 1;
500 *request_uri++ = '\0';
501 while (isspace(*request_uri)) request_uri++;
502
503 if ((http_version = strchr(request_uri, ' ')) != NULL) {
504 *http_version++ = '\0';
505 while (isspace(*http_version)) http_version++;
506 if (strncmp(http_version, HTTP_STRING, strlen(HTTP_STRING)) != 0) return 1;
507 insert_value("http-version", http_version);
508 }
509
510 insert_value("method", method);
511 insert_value("request-uri", request_uri);
512 insert_value("direction", ">");
513
514 return 0;
515 }
516
517 /* Parse a HTTP server response; bail at first sign of an invalid response */
parse_server_response(char * header_line)518 int parse_server_response(char *header_line) {
519 char *http_version, *status_code, *reason_phrase;
520
521 #ifdef DEBUG
522 ASSERT(header_line);
523 ASSERT(strlen(header_line) > 0);
524 #endif
525
526 http_version = header_line;
527
528 if ((status_code = strchr(http_version, ' ')) == NULL) return 1;
529 *status_code++ = '\0';
530 while (isspace(*status_code)) status_code++;
531
532 if ((reason_phrase = strchr(status_code, ' ')) == NULL) return 1;
533 *reason_phrase++ = '\0';
534 while (isspace(*reason_phrase)) reason_phrase++;
535
536 insert_value("http-version", http_version);
537 insert_value("status-code", status_code);
538 insert_value("reason-phrase", reason_phrase);
539 insert_value("direction", "<");
540
541 return 0;
542 }
543
544 /* Handle signals for clean reloading or shutdown */
handle_signal(int sig)545 void handle_signal(int sig) {
546
547 #ifdef DEBUG
548 ASSERT(sig > 0);
549 #endif
550
551 switch (sig) {
552 case SIGHUP:
553 LOG_PRINT("Caught SIGHUP, reloading...");
554 print_stats();
555 if (rate_stats)
556 cleanup_rate_stats();
557 open_outfiles();
558 if (rate_stats)
559 init_rate_stats(rate_interval, use_infile, rate_threshold);
560 return;
561 case SIGINT:
562 LOG_PRINT("Caught SIGINT, shutting down...");
563 print_stats();
564 cleanup();
565 break;
566 case SIGTERM:
567 LOG_PRINT("Caught SIGTERM, shutting down...");
568 print_stats();
569 cleanup();
570 break;
571 default:
572 LOG_WARN("Ignoring unknown signal '%d'", sig);
573 return;
574 }
575
576 exit(sig);
577 }
578
579 /* Perform end of run tasks and prepare to exit gracefully */
cleanup()580 void cleanup() {
581 /* This may have already been called, but might not
582 have depending on how we got here */
583 if (pcap_hnd) pcap_breakloop(pcap_hnd);
584 if (rate_stats) cleanup_rate_stats();
585
586 fflush(NULL);
587
588 free_format();
589 free_methods();
590 if (buf) free(buf);
591
592 /* Note that this won't get removed if we've switched to a
593 user that doesn't have permission to delete the file */
594 if (daemon_mode) remove(pid_filename);
595 if (pcap_hnd) pcap_close(pcap_hnd);
596
597 return;
598 }
599
600 /* Print packet capture statistics */
print_stats()601 void print_stats() {
602 struct pcap_stat pkt_stats;
603 float run_time;
604
605 if (rate_stats)
606 display_rate_stats(use_infile, rate_threshold);
607
608 if (pcap_hnd && !use_infile) {
609 if (pcap_stats(pcap_hnd, &pkt_stats) != 0) {
610 WARN("Cannot obtain packet capture statistics: %s", pcap_geterr(pcap_hnd));
611 return;
612 }
613
614 LOG_PRINT("%u packets received, %u packets dropped, %u http packets parsed", \
615 pkt_stats.ps_recv, pkt_stats.ps_drop, num_parsed);
616
617 run_time = (float) (time(0) - start_time);
618 if (run_time > 0) {
619 LOG_PRINT("%0.1f packets/min, %0.1f http packets/min", \
620 ((pkt_stats.ps_recv * 60) / run_time), ((num_parsed * 60) / run_time));
621 }
622 } else if (pcap_hnd) {
623 PRINT("%u http packets parsed", num_parsed);
624 }
625
626 return;
627 }
628
629 /* Display startup/informational banner */
display_banner()630 void display_banner() {
631 PRINT("%s version %s -- "
632 "HTTP logging and information retrieval tool", PROG_NAME, PROG_VER);
633 PRINT("Copyright (c) 2005-2014 Jason Bittel <jason.bittel@gmail.com>");
634
635 return;
636 }
637
638 /* Display program usage information */
display_usage()639 void display_usage() {
640 display_banner();
641
642 printf("Usage: %s [ -dFhpqs ] [-b file ] [ -f format ] [ -i device ] [ -l threshold ]\n"
643 " [ -m methods ] [ -n count ] [ -o file ] [ -P file ] [ -r file ]\n"
644 " [ -t seconds] [ -u user ] [ 'expression' ]\n\n", PROG_NAME);
645
646 printf(" -b file write HTTP packets to a binary dump file\n"
647 " -d run as daemon\n"
648 " -f format specify output format string\n"
649 " -F force output flush\n"
650 " -h print this help information\n"
651 " -i device listen on this interface\n"
652 " -l threshold specify a rps threshold for rate statistics\n"
653 " -m methods specify request methods to parse\n"
654 " -n count set number of HTTP packets to parse\n"
655 " -o file write output to a file\n"
656 " -p disable promiscuous mode\n"
657 " -P file use custom PID filename when running in daemon mode \n"
658 " -q suppress non-critical output\n"
659 " -r file read packets from input file\n"
660 " -s run in HTTP requests per second mode\n"
661 " -t seconds specify the display interval for rate statistics\n"
662 " -u user set process owner\n"
663 " expression specify a bpf-style capture filter\n\n");
664
665 printf("Additional information can be found at:\n"
666 " http://dumpsterventures.com/jason/httpry\n\n");
667
668 exit(EXIT_SUCCESS);
669 }
670
main(int argc,char ** argv)671 int main(int argc, char **argv) {
672 int opt;
673 extern char *optarg;
674 extern int optind;
675 int loop_status;
676
677 signal(SIGHUP, &handle_signal);
678 signal(SIGINT, &handle_signal);
679
680 /* Process command line arguments */
681 while ((opt = getopt(argc, argv, "b:df:Fhpqi:l:m:n:o:P:r:st:u:S:")) != -1) {
682 switch (opt) {
683 case 'b': use_dumpfile = optarg; break;
684 case 'd': daemon_mode = 1; use_syslog = 1; break;
685 case 'f': format_str = optarg; break;
686 case 'F': force_flush = 1; break;
687 case 'h': display_usage(); break;
688 case 'i': interface = optarg; break;
689 case 'l': rate_threshold = atoi(optarg); break;
690 case 'm': methods_str = optarg; break;
691 case 'n': parse_count = atoi(optarg); break;
692 case 'o': use_outfile = optarg; break;
693 case 'p': set_promisc = 0; break;
694 case 'P': pid_filename = optarg; break;
695 case 'q': quiet_mode = 1; break;
696 case 'r': use_infile = optarg; break;
697 case 's': rate_stats = 1; break;
698 case 't': rate_interval = atoi(optarg); break;
699 case 'u': new_user = optarg; break;
700 case 'S': eth_skip_bits = atoi(optarg); break;
701 default: display_usage();
702 }
703 }
704
705 display_banner();
706
707 if (daemon_mode && !use_outfile)
708 LOG_DIE("Daemon mode requires an output file");
709
710 if (parse_count < 0)
711 LOG_DIE("Invalid -n value, must be 0 or greater");
712
713 if (rate_interval < 1)
714 LOG_DIE("Invalid -t value, must be 1 or greater");
715
716 if (rate_threshold < 1)
717 LOG_DIE("Invalid -l value, must be 1 or greater");
718
719 if (argv[optind] && *(argv[optind])) {
720 capfilter = argv[optind];
721 } else {
722 capfilter = default_capfilter;
723 }
724
725 if (!format_str) format_str = default_format;
726 if (rate_stats) format_str = rate_format;
727 parse_format_string(format_str);
728
729 if (!methods_str) methods_str = default_methods;
730 parse_methods_string(methods_str);
731
732 if (force_flush) {
733 if (setvbuf(stdout, NULL, _IONBF, 0) != 0)
734 LOG_WARN("Cannot disable buffering on stdout");
735 }
736
737 if (!pid_filename) pid_filename = PID_FILENAME;
738
739 pcap_hnd = prepare_capture(interface, set_promisc, use_infile, capfilter);
740
741 open_outfiles();
742
743 if (daemon_mode) runas_daemon();
744 if (new_user) change_user(new_user);
745
746 if ((buf = malloc(BUFSIZ + 1)) == NULL)
747 LOG_DIE("Cannot allocate memory for packet data buffer");
748
749 if (rate_stats)
750 init_rate_stats(rate_interval, use_infile, rate_threshold);
751
752 start_time = time(0);
753 loop_status = pcap_loop(pcap_hnd, -1, &parse_http_packet, NULL);
754 if (loop_status == -1) {
755 LOG_DIE("Problem reading packets from interface: %s", pcap_geterr(pcap_hnd));
756 } else if (loop_status == -2) {
757 PRINT("Loop halted, shutting down...");
758 }
759
760 print_stats();
761 cleanup();
762
763 return loop_status == -1 ? EXIT_FAILURE : EXIT_SUCCESS;
764 }
765