Skip to content

Commit 2f68932

Browse files
authored
Merge pull request #1 from harrydevnull/feature/asynpcapdump
Feature/asynpcapdump
2 parents 49b39d7 + 83ef291 commit 2f68932

7 files changed

Lines changed: 251 additions & 170 deletions

File tree

.gitignore

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

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

examples/pcap_dump_test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

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

6-
pcap_dump = new pcap.PcapDumpSession('en0', "ip proto \\tcp",10*1024*1024,"tmp61.pcap",false,5);
6+
pcap_dump = new pcap.PcapDumpSession('en0', "ip proto \\tcp",10*1024*1024,"tmp91.pcap",false,5);
77

8-
pcap_dump.on('pcap_write_complete',function(message){
8+
pcap_dump.on('pcap_write_complete_async',function(message){
99
console.log("done.....",message);
1010
});
1111

12-
pcap_dump.start();
12+
//pcap_dump.start();
13+
pcap_dump.startAsyncCapture();

pcap_binding.cc

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,155 @@
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 PiWorker : public AsyncWorker {
25+
public:
26+
PiWorker(
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+
~PiWorker() {}
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+
}
54+
pcap_handle = pcap_create(device.c_str(), errbuf);
55+
if (pcap_handle == NULL) {
56+
Nan::ThrowError(errbuf);
57+
return;
58+
}
59+
60+
// 64KB is the max IPv4 packet size
61+
if (pcap_set_snaplen(pcap_handle, 65535) != 0) {
62+
Nan::ThrowError("error setting snaplen");
63+
return;
64+
}
65+
66+
// always use promiscuous mode
67+
if (pcap_set_promisc(pcap_handle, 1) != 0) {
68+
Nan::ThrowError("error setting promiscuous mode");
69+
return;
70+
}
71+
72+
// Try to set buffer size. Sometimes the OS has a lower limit that it will silently enforce.
73+
if (pcap_set_buffer_size(pcap_handle, buffer_size) != 0) {
74+
Nan::ThrowError("error setting buffer size");
75+
return;
76+
}
77+
78+
79+
// set "timeout" on read, even though we are also setting nonblock below. On Linux this is required.
80+
if (pcap_set_timeout(pcap_handle, 1000) != 0) {
81+
Nan::ThrowError("error setting read timeout");
82+
return;
83+
}
84+
85+
if (pcap_activate(pcap_handle) != 0) {
86+
Nan::ThrowError(pcap_geterr(pcap_handle));
87+
return;
88+
}
89+
if ((pcap_output_filename.size()) > 0) {
90+
pcap_dump_handle = pcap_dump_open(pcap_handle,pcap_output_filename.c_str());
91+
if (pcap_dump_handle == NULL) {
92+
Nan::ThrowError("error opening dump");
93+
return;
94+
}
95+
}
96+
97+
98+
if (filter.size() != 0) {
99+
if (pcap_compile(pcap_handle, &fp, filter.c_str(), 1, net) == -1) {
100+
Nan::ThrowError(pcap_geterr(pcap_handle));
101+
return;
102+
}
103+
104+
if (pcap_setfilter(pcap_handle, &fp) == -1) {
105+
Nan::ThrowError(pcap_geterr(pcap_handle));
106+
return;
107+
}
108+
109+
pcap_loop(pcap_handle, num_packets, OnPacketReady, (unsigned char *)pcap_dump_handle);
110+
pcap_freecode(&fp);
111+
}
112+
}
113+
114+
void HandleErrorCallback(){
115+
std::string error = std::string(errbuf);
116+
Local<Value> argv[] = {
117+
Nan::EmptyString()//TODO convert errbuf to error Message
118+
, Null()
119+
};
120+
121+
callback->Call(2, argv);
122+
}
123+
124+
// Executed when the async work is complete
125+
// this function will be run inside the main event loop
126+
// so it is safe to use V8 again
127+
void HandleOKCallback () {
128+
129+
Nan::HandleScope scope;
130+
131+
Local<Value> argv[] = {
132+
Null()
133+
, New<Number>(num_packets)
134+
};
135+
136+
callback->Call(2, argv);
137+
}
138+
139+
140+
141+
static void OnPacketReady(u_char *s, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
142+
pcap_dump(s, pkthdr, packet);
143+
}
144+
145+
private:
146+
147+
std::string device;
148+
std::string filter;
149+
int buffer_size;
150+
std::string pcap_output_filename;
151+
int num_packets;
152+
struct bpf_program fp;
153+
bpf_u_int32 mask;
154+
bpf_u_int32 net;
155+
pcap_t *pcap_handle;
156+
pcap_dumper_t *pcap_dump_handle;
157+
char * errbuf;
158+
159+
160+
161+
162+
};
163+
15164

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

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

149350
NODE_MODULE(pcap_binding, Initialize)

pcap_dump.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,23 @@ exports.findalldevs = function() {
3333
return binding.findalldevs();
3434
};
3535

36-
PcapDumpSession.prototype.start = function() {
36+
37+
PcapDumpSession.prototype.startAsyncCapture = function() {
3738

3839
this.opened = true;
39-
this.session.create_pcapDump(this.device_name, this.filter, this.buffer_size, this.outfile, PcapDumpSession.prototype.on_pcap_write_complete.bind(this), this.is_monitor, this.number_of_packets_to_be_read);
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+
);
4050
};
4151

52+
4253
PcapDumpSession.prototype.close = function() {
4354
this.opened = false;
4455
this.session.close();
@@ -48,19 +59,23 @@ PcapDumpSession.prototype.stats = function() {
4859
return this.session.stats();
4960
};
5061

51-
52-
PcapDumpSession.prototype.on_pcap_write_complete = function() {
53-
this.packets_read = this.packets_read + 1;
54-
if (this.packets_read >= this.number_of_packets_to_be_read) {
55-
this.emit("pcap_write_complete",{
56-
"packets_read":this.packets_read ,
62+
PcapDumpSession.prototype.on_pcap_write_complete_async = function(err,packet_count) {
63+
this.emit("pcap_write_complete_async",{
64+
"packets_read":packet_count ,
5765
"fileName":this.outfile
5866
});
59-
}
6067

6168
};
6269

6370

71+
PcapDumpSession.prototype.on_packet = function(packet) {
72+
this.emit("packet",packet);
73+
74+
};
75+
76+
77+
78+
6479
exports.PcapDumpSession = PcapDumpSession;
6580

6681

0 commit comments

Comments
 (0)