Skip to content

Commit f6585fc

Browse files
harrydevnull1hachandr
authored andcommitted
Updated the library to create pcap files
under the hood it makes use of the libpcap's capability to create pcap the add-on makes use of the Async worker
1 parent 6b422a1 commit f6585fc

File tree

11 files changed

+543
-123
lines changed

11 files changed

+543
-123
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ node_modules
3030
build
3131

3232
#OSX garbage
33-
.DS_Store
33+
.DS_Store
34+
examples/*.pcap
35+
.vscode/

examples/pcap_dump_test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
3+
4+
var pcap = require("../pcap"),
5+
6+
pcap_dump = new pcap.PcapDumpSession('en0', "ip proto \\tcp",10*1024*1024,"tmp95.pcap",false,5);
7+
8+
pcap_dump.on('pcap_write_complete_async',function(message){
9+
console.log("done.....",message);
10+
});
11+
12+
pcap_dump.on('pcap_write_error',function(message){
13+
console.log("pcap_write_error.....",message);
14+
});
15+
16+
//pcap_dump.start();
17+
pcap_dump.startAsyncCapture();

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"istanbul": "^0.3.5",
2929
"mocha": "^2.1.0",
3030
"mocha-sinon": "^1.1.4",
31+
"rewire": "^2.5.2",
3132
"should": "^5.0.0",
3233
"sinon": "^1.14.1"
3334
},

pcap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ var decode = require("./decode").decode;
66
var tcp_tracker = require("./tcp_tracker");
77
var DNSCache = require("./dns_cache");
88
var timers = require("timers");
9+
var pcap_dump = require("./pcap_dump");
910

1011
exports.decode = decode;
1112
exports.TCPTracker = tcp_tracker.TCPTracker;
1213
exports.TCPSession = tcp_tracker.TCPSession;
1314
exports.DNSCache = DNSCache;
15+
exports.PcapDumpSession = pcap_dump.PcapDumpSession;
1416

1517
function PcapSession(is_live, device_name, filter, buffer_size, outfile, is_monitor) {
1618
this.is_live = is_live;

pcap_binding.cc

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,156 @@
1212
#include "pcap_session.h"
1313

1414
using namespace v8;
15+
using Nan::Callback;
16+
using Nan::AsyncQueueWorker;
17+
using Nan::AsyncWorker;
18+
using Nan::Callback;
19+
using Nan::HandleScope;
20+
using Nan::New;
21+
using Nan::Null;
22+
using Nan::To;
23+
24+
class PcapWorker : public AsyncWorker {
25+
public:
26+
PcapWorker(
27+
Callback *callback,
28+
std::string device,
29+
std::string filter,
30+
int buffer_size,
31+
std::string pcap_output_filename,
32+
int num_packets
33+
)
34+
:
35+
AsyncWorker(callback),
36+
device(device),
37+
filter(filter),
38+
buffer_size(buffer_size),
39+
pcap_output_filename(pcap_output_filename),
40+
num_packets(num_packets)
41+
{}
42+
~PcapWorker() {}
43+
44+
// Executed inside the worker-thread.
45+
// It is not safe to access V8, or V8 data structures
46+
// here, so everything we need for input and output
47+
// should go on `this`.
48+
void Execute () {
49+
if (pcap_lookupnet(device.c_str(), &net, &mask, errbuf) == -1) {
50+
net = 0;
51+
mask = 0;
52+
fprintf(stderr, "warning: %s - this may not actually work\n", errbuf);
53+
SetErrorMessage(errbuf);
54+
return;
55+
}
56+
pcap_handle = pcap_create(device.c_str(), errbuf);
57+
if (pcap_handle == NULL) {
58+
SetErrorMessage(errbuf);
59+
return;
60+
}
61+
62+
// 64KB is the max IPv4 packet size
63+
if (pcap_set_snaplen(pcap_handle, 65535) != 0) {
64+
SetErrorMessage("error setting snaplen");
65+
return;
66+
}
67+
68+
// always use promiscuous mode
69+
if (pcap_set_promisc(pcap_handle, 1) != 0) {
70+
SetErrorMessage("error setting promiscuous mode");
71+
return;
72+
}
73+
74+
// Try to set buffer size. Sometimes the OS has a lower limit that it will silently enforce.
75+
if (pcap_set_buffer_size(pcap_handle, buffer_size) != 0) {
76+
SetErrorMessage("error setting buffer size");
77+
return;
78+
}
79+
80+
81+
// set "timeout" on read, even though we are also setting nonblock below. On Linux this is required.
82+
if (pcap_set_timeout(pcap_handle, 1000) != 0) {
83+
SetErrorMessage("error setting read timeout");
84+
return;
85+
}
86+
87+
if (pcap_activate(pcap_handle) != 0) {
88+
SetErrorMessage(pcap_geterr(pcap_handle));
89+
return;
90+
}
91+
if ((pcap_output_filename.size()) > 0) {
92+
pcap_dump_handle = pcap_dump_open(pcap_handle,pcap_output_filename.c_str());
93+
if (pcap_dump_handle == NULL) {
94+
SetErrorMessage("error opening dump");
95+
return;
96+
}
97+
}
98+
99+
100+
if (filter.size() != 0) {
101+
if (pcap_compile(pcap_handle, &fp, filter.c_str(), 1, net) == -1) {
102+
SetErrorMessage(pcap_geterr(pcap_handle));
103+
return;
104+
}
105+
106+
if (pcap_setfilter(pcap_handle, &fp) == -1) {
107+
SetErrorMessage(pcap_geterr(pcap_handle));
108+
return;
109+
}
110+
111+
pcap_loop(pcap_handle, num_packets, OnPacketReady, (unsigned char *)pcap_dump_handle);
112+
pcap_freecode(&fp);
113+
/*
114+
* Close the savefile opened in pcap_dump_open().
115+
*/
116+
pcap_dump_close(pcap_dump_handle);
117+
/*
118+
* Close the packet capture device and free the memory used by the
119+
* packet capture descriptor.
120+
*/
121+
pcap_close(pcap_handle);
122+
}
123+
}
124+
125+
// Executed when the async work is complete
126+
// this function will be run inside the main event loop
127+
// so it is safe to use V8 again
128+
void HandleOKCallback () {
129+
130+
Nan::HandleScope scope;
131+
132+
Local<Value> argv[] = {
133+
Nan::Null()
134+
, New<Number>(num_packets)
135+
};
136+
137+
callback->Call(2, argv);
138+
}
139+
140+
141+
142+
static void OnPacketReady(u_char *s, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
143+
pcap_dump(s, pkthdr, packet);
144+
}
145+
146+
private:
147+
148+
std::string device;
149+
std::string filter;
150+
int buffer_size;
151+
std::string pcap_output_filename;
152+
int num_packets;
153+
struct bpf_program fp;
154+
bpf_u_int32 mask;
155+
bpf_u_int32 net;
156+
pcap_t *pcap_handle;
157+
pcap_dumper_t *pcap_dump_handle;
158+
char errbuf[PCAP_ERRBUF_SIZE];
159+
160+
161+
162+
163+
};
164+
15165

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

288+
// Asynchronous access to the `Estimate()` function
289+
NAN_METHOD(PcapDumpAsync) {
290+
291+
if (info.Length() == 8) {
292+
if (!info[0]->IsString()) {
293+
Nan::ThrowTypeError("pcap Open: info[0] must be a String");
294+
return;
295+
}
296+
if (!info[1]->IsString()) {
297+
Nan::ThrowTypeError("pcap Open: info[1] must be a String");
298+
return;
299+
}
300+
if (!info[2]->IsInt32()) {
301+
Nan::ThrowTypeError("pcap Open: info[2] must be a Number");
302+
return;
303+
}
304+
if (!info[3]->IsString()) {
305+
Nan::ThrowTypeError("pcap Open: info[3] must be a String");
306+
return;
307+
}
308+
if (!info[4]->IsFunction()) {
309+
Nan::ThrowTypeError("pcap Open: info[4] must be a Function");
310+
return;
311+
}
312+
if (!info[5]->IsBoolean()) {
313+
Nan::ThrowTypeError("pcap Open: info[5] must be a Boolean");
314+
return;
315+
}
316+
if (!info[6]->IsInt32()) {
317+
Nan::ThrowTypeError("pcap Open: info[6] must be a Number");
318+
return;
319+
}
320+
if (!info[7]->IsFunction()) {
321+
Nan::ThrowTypeError("pcap Open: info[7] must be a Function");
322+
return;
323+
}
324+
} else {
325+
Nan::ThrowTypeError("pcap CreatePcapDump: expecting 7 arguments");
326+
return;
327+
}
328+
Nan::Utf8String device(info[0]->ToString());
329+
Nan::Utf8String filter(info[1]->ToString());
330+
int buffer_size = info[2]->Int32Value();
331+
Nan::Utf8String pcap_output_filename(info[3]->ToString());
332+
int num_packets = info[6]->Int32Value();
333+
Callback *callback = new Callback(info[7].As<Function>());
334+
335+
AsyncQueueWorker(new PcapWorker(callback, std::string(*device),std::string(*filter),buffer_size, std::string(*pcap_output_filename),num_packets));
336+
}
337+
338+
138339
void Initialize(Handle<Object> exports)
139340
{
140341
Nan::HandleScope scope;
@@ -144,6 +345,7 @@ void Initialize(Handle<Object> exports)
144345
exports->Set(Nan::New("findalldevs").ToLocalChecked(), Nan::New<FunctionTemplate>(FindAllDevs)->GetFunction());
145346
exports->Set(Nan::New("default_device").ToLocalChecked(), Nan::New<FunctionTemplate>(DefaultDevice)->GetFunction());
146347
exports->Set(Nan::New("lib_version").ToLocalChecked(), Nan::New<FunctionTemplate>(LibVersion)->GetFunction());
348+
exports->Set(Nan::New("create_pcap_dump_async").ToLocalChecked(), Nan::New<FunctionTemplate>(PcapDumpAsync)->GetFunction());
147349
}
148350

149351
NODE_MODULE(pcap_binding, Initialize)

pcap_dump.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
var util = require("util");
2+
var events = require("events");
3+
var binding = require("./build/Release/pcap_binding");
4+
5+
6+
function PcapDumpSession(device_name, filter, buffer_size, outfile, is_monitor, number_of_packets_to_be_read) {
7+
8+
this.device_name = device_name;
9+
this.filter = filter || "";
10+
this.buffer_size = buffer_size;
11+
this.outfile = outfile || "tmp.pcap";
12+
this.is_monitor = Boolean(is_monitor);
13+
this.opened = null;
14+
this.packets_read = 0;
15+
this.number_of_packets_to_be_read = number_of_packets_to_be_read || 1;
16+
this.session = new binding.PcapSession();
17+
18+
if (typeof this.buffer_size === "number" && !isNaN(this.buffer_size)) {
19+
this.buffer_size = Math.round(this.buffer_size);
20+
} else {
21+
this.buffer_size = 10 * 1024 * 1024; // Default buffer size is 10MB
22+
}
23+
24+
this.device_name = this.device_name || binding.default_device();
25+
events.EventEmitter.call(this);
26+
}
27+
28+
util.inherits(PcapDumpSession, events.EventEmitter);
29+
30+
exports.lib_version = binding.lib_version();
31+
32+
exports.findalldevs = function () {
33+
return binding.findalldevs();
34+
};
35+
36+
37+
PcapDumpSession.prototype.startAsyncCapture = function () {
38+
39+
this.opened = true;
40+
binding.create_pcap_dump_async(
41+
this.device_name,
42+
this.filter,
43+
this.buffer_size,
44+
this.outfile,
45+
PcapDumpSession.prototype.on_packet.bind(this),
46+
this.is_monitor,
47+
this.number_of_packets_to_be_read,
48+
PcapDumpSession.prototype.on_pcap_write_complete_async.bind(this)
49+
);
50+
};
51+
52+
53+
PcapDumpSession.prototype.close = function () {
54+
this.opened = false;
55+
this.session.close();
56+
};
57+
58+
PcapDumpSession.prototype.stats = function () {
59+
return this.session.stats();
60+
};
61+
62+
PcapDumpSession.prototype.on_pcap_write_complete_async = function (err, packet_count) {
63+
64+
if (err) {
65+
this.emit("pcap_write_error", err);
66+
67+
} else {
68+
this.emit("pcap_write_complete_async", {
69+
"packets_read": packet_count,
70+
"fileName": this.outfile
71+
});
72+
}
73+
74+
};
75+
76+
77+
PcapDumpSession.prototype.on_packet = function (packet) {
78+
this.emit("packet", packet);
79+
80+
};
81+
82+
83+
84+
85+
exports.PcapDumpSession = PcapDumpSession;
86+
87+
88+
exports.createPcapDumpSession = function (device, filter, buffer_size, path, monitor, number_of_packets) {
89+
return new PcapDumpSession(device, filter, buffer_size, path, monitor, number_of_packets);
90+
};

0 commit comments

Comments
 (0)