Skip to content

Commit

Permalink
Updated the library to create pcap files
Browse files Browse the repository at this point in the history
under the hood it makes use of the libpcap's capability to create pcap
the add-on makes use of the Async worker
  • Loading branch information
harrydevnull authored and 1hachandr committed Sep 30, 2016
1 parent 6b422a1 commit f6585fc
Show file tree
Hide file tree
Showing 11 changed files with 543 additions and 123 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ node_modules
build

#OSX garbage
.DS_Store
.DS_Store
examples/*.pcap
.vscode/
17 changes: 17 additions & 0 deletions examples/pcap_dump_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@



var pcap = require("../pcap"),

pcap_dump = new pcap.PcapDumpSession('en0', "ip proto \\tcp",10*1024*1024,"tmp95.pcap",false,5);

pcap_dump.on('pcap_write_complete_async',function(message){
console.log("done.....",message);
});

pcap_dump.on('pcap_write_error',function(message){
console.log("pcap_write_error.....",message);
});

//pcap_dump.start();
pcap_dump.startAsyncCapture();
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"istanbul": "^0.3.5",
"mocha": "^2.1.0",
"mocha-sinon": "^1.1.4",
"rewire": "^2.5.2",
"should": "^5.0.0",
"sinon": "^1.14.1"
},
Expand Down
2 changes: 2 additions & 0 deletions pcap.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ var decode = require("./decode").decode;
var tcp_tracker = require("./tcp_tracker");
var DNSCache = require("./dns_cache");
var timers = require("timers");
var pcap_dump = require("./pcap_dump");

exports.decode = decode;
exports.TCPTracker = tcp_tracker.TCPTracker;
exports.TCPSession = tcp_tracker.TCPSession;
exports.DNSCache = DNSCache;
exports.PcapDumpSession = pcap_dump.PcapDumpSession;

function PcapSession(is_live, device_name, filter, buffer_size, outfile, is_monitor) {
this.is_live = is_live;
Expand Down
202 changes: 202 additions & 0 deletions pcap_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,156 @@
#include "pcap_session.h"

using namespace v8;
using Nan::Callback;
using Nan::AsyncQueueWorker;
using Nan::AsyncWorker;
using Nan::Callback;
using Nan::HandleScope;
using Nan::New;
using Nan::Null;
using Nan::To;

class PcapWorker : public AsyncWorker {
public:
PcapWorker(
Callback *callback,
std::string device,
std::string filter,
int buffer_size,
std::string pcap_output_filename,
int num_packets
)
:
AsyncWorker(callback),
device(device),
filter(filter),
buffer_size(buffer_size),
pcap_output_filename(pcap_output_filename),
num_packets(num_packets)
{}
~PcapWorker() {}

// Executed inside the worker-thread.
// It is not safe to access V8, or V8 data structures
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
if (pcap_lookupnet(device.c_str(), &net, &mask, errbuf) == -1) {
net = 0;
mask = 0;
fprintf(stderr, "warning: %s - this may not actually work\n", errbuf);
SetErrorMessage(errbuf);
return;
}
pcap_handle = pcap_create(device.c_str(), errbuf);
if (pcap_handle == NULL) {
SetErrorMessage(errbuf);
return;
}

// 64KB is the max IPv4 packet size
if (pcap_set_snaplen(pcap_handle, 65535) != 0) {
SetErrorMessage("error setting snaplen");
return;
}

// always use promiscuous mode
if (pcap_set_promisc(pcap_handle, 1) != 0) {
SetErrorMessage("error setting promiscuous mode");
return;
}

// Try to set buffer size. Sometimes the OS has a lower limit that it will silently enforce.
if (pcap_set_buffer_size(pcap_handle, buffer_size) != 0) {
SetErrorMessage("error setting buffer size");
return;
}


// set "timeout" on read, even though we are also setting nonblock below. On Linux this is required.
if (pcap_set_timeout(pcap_handle, 1000) != 0) {
SetErrorMessage("error setting read timeout");
return;
}

if (pcap_activate(pcap_handle) != 0) {
SetErrorMessage(pcap_geterr(pcap_handle));
return;
}
if ((pcap_output_filename.size()) > 0) {
pcap_dump_handle = pcap_dump_open(pcap_handle,pcap_output_filename.c_str());
if (pcap_dump_handle == NULL) {
SetErrorMessage("error opening dump");
return;
}
}


if (filter.size() != 0) {
if (pcap_compile(pcap_handle, &fp, filter.c_str(), 1, net) == -1) {
SetErrorMessage(pcap_geterr(pcap_handle));
return;
}

if (pcap_setfilter(pcap_handle, &fp) == -1) {
SetErrorMessage(pcap_geterr(pcap_handle));
return;
}

pcap_loop(pcap_handle, num_packets, OnPacketReady, (unsigned char *)pcap_dump_handle);
pcap_freecode(&fp);
/*
* Close the savefile opened in pcap_dump_open().
*/
pcap_dump_close(pcap_dump_handle);
/*
* Close the packet capture device and free the memory used by the
* packet capture descriptor.
*/
pcap_close(pcap_handle);
}
}

// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use V8 again
void HandleOKCallback () {

Nan::HandleScope scope;

Local<Value> argv[] = {
Nan::Null()
, New<Number>(num_packets)
};

callback->Call(2, argv);
}



static void OnPacketReady(u_char *s, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
pcap_dump(s, pkthdr, packet);
}

private:

std::string device;
std::string filter;
int buffer_size;
std::string pcap_output_filename;
int num_packets;
struct bpf_program fp;
bpf_u_int32 mask;
bpf_u_int32 net;
pcap_t *pcap_handle;
pcap_dumper_t *pcap_dump_handle;
char errbuf[PCAP_ERRBUF_SIZE];




};


// Helper method, convert a sockaddr* (AF_INET or AF_INET6) to a string, and set it as the property
// named 'key' in the Address object you pass in.
Expand Down Expand Up @@ -135,6 +285,57 @@ NAN_METHOD(LibVersion)
info.GetReturnValue().Set(Nan::New(pcap_lib_version()).ToLocalChecked());
}

// Asynchronous access to the `Estimate()` function
NAN_METHOD(PcapDumpAsync) {

if (info.Length() == 8) {
if (!info[0]->IsString()) {
Nan::ThrowTypeError("pcap Open: info[0] must be a String");
return;
}
if (!info[1]->IsString()) {
Nan::ThrowTypeError("pcap Open: info[1] must be a String");
return;
}
if (!info[2]->IsInt32()) {
Nan::ThrowTypeError("pcap Open: info[2] must be a Number");
return;
}
if (!info[3]->IsString()) {
Nan::ThrowTypeError("pcap Open: info[3] must be a String");
return;
}
if (!info[4]->IsFunction()) {
Nan::ThrowTypeError("pcap Open: info[4] must be a Function");
return;
}
if (!info[5]->IsBoolean()) {
Nan::ThrowTypeError("pcap Open: info[5] must be a Boolean");
return;
}
if (!info[6]->IsInt32()) {
Nan::ThrowTypeError("pcap Open: info[6] must be a Number");
return;
}
if (!info[7]->IsFunction()) {
Nan::ThrowTypeError("pcap Open: info[7] must be a Function");
return;
}
} else {
Nan::ThrowTypeError("pcap CreatePcapDump: expecting 7 arguments");
return;
}
Nan::Utf8String device(info[0]->ToString());
Nan::Utf8String filter(info[1]->ToString());
int buffer_size = info[2]->Int32Value();
Nan::Utf8String pcap_output_filename(info[3]->ToString());
int num_packets = info[6]->Int32Value();
Callback *callback = new Callback(info[7].As<Function>());

AsyncQueueWorker(new PcapWorker(callback, std::string(*device),std::string(*filter),buffer_size, std::string(*pcap_output_filename),num_packets));
}


void Initialize(Handle<Object> exports)
{
Nan::HandleScope scope;
Expand All @@ -144,6 +345,7 @@ void Initialize(Handle<Object> exports)
exports->Set(Nan::New("findalldevs").ToLocalChecked(), Nan::New<FunctionTemplate>(FindAllDevs)->GetFunction());
exports->Set(Nan::New("default_device").ToLocalChecked(), Nan::New<FunctionTemplate>(DefaultDevice)->GetFunction());
exports->Set(Nan::New("lib_version").ToLocalChecked(), Nan::New<FunctionTemplate>(LibVersion)->GetFunction());
exports->Set(Nan::New("create_pcap_dump_async").ToLocalChecked(), Nan::New<FunctionTemplate>(PcapDumpAsync)->GetFunction());
}

NODE_MODULE(pcap_binding, Initialize)
90 changes: 90 additions & 0 deletions pcap_dump.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
var util = require("util");
var events = require("events");
var binding = require("./build/Release/pcap_binding");


function PcapDumpSession(device_name, filter, buffer_size, outfile, is_monitor, number_of_packets_to_be_read) {

this.device_name = device_name;
this.filter = filter || "";
this.buffer_size = buffer_size;
this.outfile = outfile || "tmp.pcap";
this.is_monitor = Boolean(is_monitor);
this.opened = null;
this.packets_read = 0;
this.number_of_packets_to_be_read = number_of_packets_to_be_read || 1;
this.session = new binding.PcapSession();

if (typeof this.buffer_size === "number" && !isNaN(this.buffer_size)) {
this.buffer_size = Math.round(this.buffer_size);
} else {
this.buffer_size = 10 * 1024 * 1024; // Default buffer size is 10MB
}

this.device_name = this.device_name || binding.default_device();
events.EventEmitter.call(this);
}

util.inherits(PcapDumpSession, events.EventEmitter);

exports.lib_version = binding.lib_version();

exports.findalldevs = function () {
return binding.findalldevs();
};


PcapDumpSession.prototype.startAsyncCapture = function () {

this.opened = true;
binding.create_pcap_dump_async(
this.device_name,
this.filter,
this.buffer_size,
this.outfile,
PcapDumpSession.prototype.on_packet.bind(this),
this.is_monitor,
this.number_of_packets_to_be_read,
PcapDumpSession.prototype.on_pcap_write_complete_async.bind(this)
);
};


PcapDumpSession.prototype.close = function () {
this.opened = false;
this.session.close();
};

PcapDumpSession.prototype.stats = function () {
return this.session.stats();
};

PcapDumpSession.prototype.on_pcap_write_complete_async = function (err, packet_count) {

if (err) {
this.emit("pcap_write_error", err);

} else {
this.emit("pcap_write_complete_async", {
"packets_read": packet_count,
"fileName": this.outfile
});
}

};


PcapDumpSession.prototype.on_packet = function (packet) {
this.emit("packet", packet);

};




exports.PcapDumpSession = PcapDumpSession;


exports.createPcapDumpSession = function (device, filter, buffer_size, path, monitor, number_of_packets) {
return new PcapDumpSession(device, filter, buffer_size, path, monitor, number_of_packets);
};
Loading

0 comments on commit f6585fc

Please sign in to comment.