Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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