Permalink
Browse files

Merge remote branch 'origin/master' into trace

Conflicts:
	pcap.js
  • Loading branch information...
2 parents cb80a5e + da3c628 commit dfdd962266daa2c030eeabf42dd4388484450703 @mranney mranney committed Jul 16, 2010
Showing with 635 additions and 549 deletions.
  1. +15 −45 README.md
  2. +126 −35 examples/http_trace
  3. +1 −1 package.json
  4. +493 −468 pcap.js
View
@@ -34,7 +34,11 @@ or just send me a patch.
You will need `libpcap` installed. Most OSX machines seem to have it. All major Linux distributions have it available
either by default or with a package like `libpcap-dev`.
-Clone the repo somehow, probably like this:
+The easiest way to get `node_pcap` and its tools is with `npm`:
+
+ npm install pcap
+
+If you want to hack on the source code, you can get it from github. Clone the repo like this:
git clone git://github.com/mranney/node_pcap.git
@@ -89,52 +93,18 @@ Unless you are root already, you'll probably want to start your node program lik
sudo node test.js
-## examples/http_trace.js
+## examples/http_trace
+
+This is a handy standalone program that can help diagnose HTTP traffic.
The TCP tracker looks for HTTP at the beginning of every TCP connection. If found, all captured on this connection
-will be fed to node's HTTP parser and events will be generated. `examples/http_trace.js` is an example of this.
-
-Listen to all TCP connections for HTTP, then open `nodejs.org` in Safari:
-
- rv-mjr2:~/work/node_pcap (master)$ sudo node examples/http_trace.js en1 tcp
- Listening on en1
- rv-mjr2.ranney.com:54784 -> tinyclouds.org:80 #1 HTTP 1.1 request: GET /
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response: 200 OK
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response body: 10 bytes
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response body: 1178 bytes
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response body: 1448 bytes
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response body: 1448 bytes
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response body: 210 bytes
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #1 HTTP 1.1 response complete 4.19KB
- rv-mjr2.ranney.com:54785 -> tinyclouds.org:80 #1 HTTP 1.1 request: GET /sh_main.js
- rv-mjr2.ranney.com:54786 -> tinyclouds.org:80 #1 HTTP 1.1 request: GET /sh_vim-dark.css
- rv-mjr2.ranney.com:54788 -> tinyclouds.org:80 #1 HTTP 1.1 request: GET /sh_javascript.min.js
- rv-mjr2.ranney.com:54787 -> tinyclouds.org:80 #1 HTTP 1.1 request: GET /pipe.css
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54785 #1 HTTP 1.1 response: 304 Not Modified
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54785 #1 HTTP 1.1 response complete 0.00KB
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54786 #1 HTTP 1.1 response: 304 Not Modified
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54786 #1 HTTP 1.1 response complete 0.00KB
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54788 #1 HTTP 1.1 response: 304 Not Modified
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54788 #1 HTTP 1.1 response complete 0.00KB
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54787 #1 HTTP 1.1 response: 304 Not Modified
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54787 #1 HTTP 1.1 response complete 0.00KB
- rv-mjr2.ranney.com:54784 -> tinyclouds.org:80 #2 HTTP 1.1 request: GET /logo.png
- rv-mjr2.ranney.com:54789 -> 74.125.53.138:80 #1 HTTP 1.1 request: GET /ga.js
- 74.125.53.138:80 -> rv-mjr2.ranney.com:54789 #1 HTTP 1.1 response: 304 Not Modified
- 74.125.53.138:80 -> rv-mjr2.ranney.com:54789 #1 HTTP 1.1 response complete 0.00KB
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #2 HTTP 1.1 response: 304 Not Modified
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #2 HTTP 1.1 response complete 0.00KB
- rv-mjr2.ranney.com:54789 -> 74.125.53.138:80 #2 HTTP 1.1 request: GET /__utm.gif?utmwv=4.7.2&utmn=1295457147&utmhn=nodejs.org&utmcs=UTF-8&utmsr=2560x1600&utmsc=24-bit&utmul=en-us&utmje=1&utmfl=10.0%20r42&utmdt=node.js&utmhid=303497140&utmr=-&utmp=%2F&utmac=UA-10874194-2&utmcc=__utma%3D212211339.882401248.1271227610.1276063650.1276069881.7%3B%2B__utmz%3D212211339.1271227610.1.1.utmcsr%3D(direct)%7Cutmccn%3D(direct)%7Cutmcmd%3D(none)%3B
- 74.125.53.138:80 -> rv-mjr2.ranney.com:54789 #2 HTTP 1.1 response: 200 OK
- 74.125.53.138:80 -> rv-mjr2.ranney.com:54789 #2 HTTP 1.1 response body: 35 bytes
- 74.125.53.138:80 -> rv-mjr2.ranney.com:54789 #2 HTTP 1.1 response complete 0.03KB
- rv-mjr2.ranney.com:54784 -> tinyclouds.org:80 #3 HTTP 1.1 request: GET /favicon.ico
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #3 HTTP 1.1 response: 404 Not Found
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #3 HTTP 1.1 response body: 169 bytes
- tinyclouds.org:80 -> rv-mjr2.ranney.com:54784 #3 HTTP 1.1 response complete 0.17KB
-
-
-## examples/simple_capture.js
+will be fed to node's HTTP parser and events will be generated. `http_trace` has listeners for these events and will
+print out some helpful information.
+
+![http_trace screenshot](http://ranney.com/httptrace.jpg)
+
+
+## examples/simple_capture
This program captures packets and prints them as best it can. Here's a sample of it's output.
In another window I ran `curl nodejs.org`.
View
@@ -4,78 +4,169 @@
var sys = require("sys"),
node_http = require('http'),
+ node_url = require('url'),
pcap = require("pcap"), pcap_session,
dns_cache = pcap.dns_cache,
- tcp_tracker = new pcap.TCP_tracker();
+ tcp_tracker = new pcap.TCP_tracker(),
+ ANSI;
+ANSI = (function () {
+ // http://en.wikipedia.org/wiki/ANSI_escape_code
+ var formats = {
+ bold: [1, 22], // bright
+ light: [2, 22], // faint
+ italic: [3, 23],
+ underline: [4, 24], // underline single
+ blink_slow: [5, 25],
+ blink_fast: [6, 25],
+ inverse: [7, 27],
+ conceal: [8, 28],
+ strikethrough: [9, 29], // crossed-out
+ // 10 - 20 are font control
+ underline_double: [21, 24],
+ black: [30, 39],
+ red: [31, 39],
+ green: [32, 39],
+ yellow: [33, 39],
+ blue: [34, 39],
+ magenta: [35, 39],
+ cyan: [36, 39],
+ white: [37, 39],
+ grey: [90, 39]
+ };
+
+ var CSI = String.fromCharCode(27) + '[';
+
+ return function (str, format) {
+ return CSI + formats[format][0] + 'm' + str + CSI + formats[format][1] + 'm';
+ };
+}());
+
+function lpad(num, len) {
+ var str = num.toString();
+
+ while (str.length < len) {
+ str = "0" + str;
+ }
+ return str;
+}
+
+function format_timestamp(timems) {
+ var date_obj = new Date(timems);
+
+ return ANSI(lpad(date_obj.getHours(), 2) + ":" + lpad(date_obj.getMinutes(), 2) + ":" + lpad(date_obj.getSeconds(), 2) + "." +
+ lpad(date_obj.getMilliseconds(), 3), "blue");
+}
+
+function format_hostname(hostname) {
+ if (/[a-zA-Z]/.test(hostname)) {
+ var parts = hostname.split(":");
+ return ANSI(parts[0].split('.')[0] + ":" + parts[1], "magenta");
+ } else {
+ return ANSI(hostname, "magenta");
+ }
+}
+
+function format_line_start(ts, src, dst) {
+ return format_timestamp(ts) + " " + format_hostname(src) + " -> " + format_hostname(dst);
+}
+
+function format_headers(headers) {
+ return Object.keys(headers).map(function (val) {
+ if (val === "Cookie") {
+ var cookie_pairs = headers[val].split("; ").sort();
+ return(" " + ANSI(val, "white") + ": " + ANSI(cookie_pairs.map(function (pair) {
+ var parts = pair.split('=');
+ return parts[0] + ": " + parts[1];
+ }).join("\n "), "grey"));
+ } else {
+ return(" " + ANSI(val, "white") + ": " + ANSI(headers[val], "grey"));
+ }
+ }).join("\n");
+}
+
+function format_size(size) {
+ if (size < 1024 * 2) {
+ return size + "B";
+ } else if (size < 1024 * 1024 * 2) {
+ return (size / 1024).toFixed(2) + "KB";
+ } else {
+ return (size / 1024 / 1024).toFixed(2) + "MB"
+ }
+}
+
if (process.argv.length !== 4) {
sys.error("usage: " + process.argv[1] + " interface filter");
sys.error("Examples: ");
- sys.error(' sudo node http_trace.js en0 "tcp port 80"');
- sys.error(' sudo node http_trace.js eth1 ""');
- sys.error(' sudo node http_trace.js lo0 "ip proto \\tcp and tcp port 80"');
+ sys.error(' sudo http_trace.js en0 "tcp port 80"');
+ sys.error(' sudo http_trace.js eth1 ""');
+ sys.error(' sudo http_trace.js lo0 "ip proto \\tcp and tcp port 80"');
process.exit(1);
}
pcap_session = pcap.createSession(process.argv[2], process.argv[3]);
-sys.puts("Listening on " + pcap_session.device_name);
+console.log("Listening on " + pcap_session.device_name);
// Check for pcap dropped packets on an interval
setInterval(function () {
var stats = pcap_session.stats();
if (stats.ps_drop > 0) {
- sys.puts("pcap dropped packets: " + sys.inspect(stats));
+ sys.puts(ANSI("pcap dropped packets: " + sys.inspect(stats), bold));
}
}, 2000);
-tcp_tracker.addListener('http_request', function (session, http) {
+tcp_tracker.on('http_request', function (session, http) {
if (session.http_request_count) {
session.http_request_count += 1;
} else {
session.http_request_count = 1;
}
- sys.puts(session.src_name + " -> " + session.dst_name + " #" + session.http_request_count +
- " HTTP " + http.request.http_version + " request: " +
- http.request.method + " " + http.request.url);
- Object.keys(http.request.headers).forEach(function (v) {
- sys.puts(" " + v + ": " + http.request.headers[v]);
- });
+ console.log(format_line_start(session.current_cap_time, session.src_name, session.dst_name) +
+ " #" + session.http_request_count + " HTTP " + http.request.http_version + " request: " +
+ ANSI(ANSI(http.request.method, "bold") + " " + http.request.url, "yellow"));
+ console.log(format_headers(http.request.headers));
});
-tcp_tracker.addListener('http_request_body', function (session, http, data) {
- sys.puts(session.src_name + " -> " + session.dst_name + " #" + session.http_request_count +
- " HTTP " + http.request.http_version + " request body: " +
- data.length + " bytes");
+tcp_tracker.on('http_request_body', function (session, http, data) {
+ console.log(format_line_start(session.current_cap_time, session.src_name, session.dst_name) +
+ " #" + session.http_request_count + " HTTP " + http.request.http_version + " request body: " +
+ format_size(data.length));
+ if (/application\/x-www-form-urlencoded/.test(http.request.headers["Content-Type"])) {
+ console.log(ANSI(data.toString("utf8"), "green"));
+ }
});
-tcp_tracker.addListener('http_request_complete', function (session, http, data) {
- sys.puts(session.src_name + " -> " + session.dst_name + " #" + session.http_request_count +
- " HTTP " + http.request.http_version + " request complete " + (http.request.body_len / 1024).toFixed(2) + "KB");
+tcp_tracker.on('http_request_complete', function (session, http, data) {
+ console.log(format_line_start(session.current_cap_time, session.src_name, session.dst_name) +
+ " #" + session.http_request_count + " HTTP " + http.request.http_version + " request complete " +
+ format_size(http.request.body_len));
});
-tcp_tracker.addListener('http_response', function (session, http) {
- sys.puts(session.dst_name + " -> " + session.src_name + " #" + session.http_request_count +
- " HTTP " + http.response.http_version + " response: " + http.response.status_code + " " + node_http.STATUS_CODES[http.response.status_code]);
- Object.keys(http.response.headers).forEach(function (v) {
- sys.puts(" " + v + ": " + http.response.headers[v]);
- });
+tcp_tracker.on('http_response', function (session, http) {
+ console.log(format_line_start(session.current_cap_time, session.dst_name, session.src_name) +
+ " #" + session.http_request_count + " HTTP " + http.response.http_version + " response: " +
+ ANSI(http.response.status_code + " " + node_http.STATUS_CODES[http.response.status_code], "yellow"));
+ console.log(format_headers(http.response.headers));
});
-tcp_tracker.addListener('http_response_body', function (session, http, data) {
- sys.puts(session.dst_name + " -> " + session.src_name + " #" + session.http_request_count +
- " HTTP " + http.response.http_version + " response body: " +
- data.length + " bytes");
-// sys.puts(data);
+tcp_tracker.on('http_response_body', function (session, http, data) {
+ console.log(format_line_start(session.current_cap_time, session.dst_name, session.src_name) +
+ " #" + session.http_request_count + " HTTP " + http.response.http_version + " response body: " +
+ format_size(data.length));
+ if (!http.response.headers["Content-Encoding"] && /^text/.test(http.response.headers["Content-Type"])) {
+ console.log(ANSI(data.toString("utf8"), "green"));
+ }
});
-tcp_tracker.addListener('http_response_complete', function (session, http, data) {
- sys.puts(session.dst_name + " -> " + session.src_name + " #" + session.http_request_count +
- " HTTP " + http.response.http_version + " response complete " + (http.response.body_len / 1024).toFixed(2) + "KB");
+tcp_tracker.on('http_response_complete', function (session, http, data) {
+ console.log(format_line_start(session.current_cap_time, session.dst_name, session.src_name) +
+ " #" + session.http_request_count + " HTTP " + http.response.http_version + " response complete " +
+ format_size(http.response.body_len));
});
// listen for packets, decode them, and feed TCP to the tracker
-pcap_session.addListener('packet', function (raw_packet) {
+pcap_session.on('packet', function (raw_packet) {
var packet = pcap.decode.packet(raw_packet);
tcp_tracker.track_packet(packet);
View
@@ -1,5 +1,5 @@
{ "name" : "pcap",
- "version" : "0.0.6",
+ "version" : "0.0.7",
"description" : "raw packet capture, decoding, and analysis",
"author": "Matt Ranney <mjr@ranney.com>",
"main": "./pcap",
Oops, something went wrong.

0 comments on commit dfdd962

Please sign in to comment.