pcaprub 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +20 -6
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/ext/pcaprub/extconf.rb +7 -0
- data/ext/pcaprub/pcaprub.c +186 -38
- data/pcaprub.gemspec +1 -1
- data/test/test_pcaprub_unit.rb +14 -5
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -10,17 +10,31 @@ PacketRub project (http://packetrub.rubyforge.org).
|
|
10
10
|
Requirements:
|
11
11
|
libpcap - http://www.tcpdump.org
|
12
12
|
|
13
|
-
==
|
13
|
+
== Installation
|
14
14
|
|
15
|
-
|
16
|
-
svn checkout http://pcaprub.rubyforge.org/svn/trunk/
|
15
|
+
gem install pcaprub
|
17
16
|
|
18
|
-
The Metasploit Project also provides a Subversion repository:
|
19
|
-
svn checkout http://metasploit.com/svn/framework3/trunk/external/pcaprub/
|
20
17
|
|
21
|
-
|
18
|
+
== Usage
|
19
|
+
|
20
|
+
require 'rubygems'
|
21
|
+
require 'pcaprub'
|
22
|
+
|
23
|
+
cap = Pcap.new
|
22
24
|
|
23
25
|
== Current Repository for Gemcutter source
|
24
26
|
|
25
27
|
The Git Repo on Github @shadowbq is forked from the Metasploit SVN repo
|
26
28
|
git clone git://github.com/shadowbq/pcaprub.git
|
29
|
+
|
30
|
+
|
31
|
+
=== Notes on other repositories
|
32
|
+
|
33
|
+
The latest community svn version can be obtained from Subversion:
|
34
|
+
svn checkout http://pcaprub.rubyforge.org/svn/trunk/
|
35
|
+
|
36
|
+
The Metasploit Project also provides a Subversion repository:
|
37
|
+
svn checkout http://metasploit.com/svn/framework3/trunk/external/pcaprub/
|
38
|
+
|
39
|
+
(credits: github.com/dxoigmn/pcaprub)
|
40
|
+
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/ext/pcaprub/extconf.rb
CHANGED
data/ext/pcaprub/pcaprub.c
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
#include "ruby.h"
|
2
|
+
|
3
|
+
#ifndef RUBY_19
|
2
4
|
#include "rubysig.h"
|
5
|
+
#endif
|
6
|
+
|
3
7
|
|
4
8
|
#include <pcap.h>
|
5
9
|
|
@@ -8,15 +12,18 @@
|
|
8
12
|
#include <arpa/inet.h>
|
9
13
|
#endif
|
10
14
|
|
15
|
+
#include <sys/time.h>
|
16
|
+
|
11
17
|
static VALUE rb_cPcap;
|
12
18
|
|
13
|
-
#define PCAPRUB_VERSION "0.
|
19
|
+
#define PCAPRUB_VERSION "0.9-dev"
|
14
20
|
|
15
21
|
#define OFFLINE 1
|
16
22
|
#define LIVE 2
|
17
23
|
|
18
24
|
typedef struct rbpcap {
|
19
25
|
pcap_t *pd;
|
26
|
+
pcap_dumper_t *pdt;
|
20
27
|
char iface[256];
|
21
28
|
char type;
|
22
29
|
} rbpcap_t;
|
@@ -24,7 +31,7 @@ typedef struct rbpcap {
|
|
24
31
|
|
25
32
|
typedef struct rbpcapjob {
|
26
33
|
struct pcap_pkthdr hdr;
|
27
|
-
char *pkt;
|
34
|
+
unsigned char *pkt;
|
28
35
|
int wtf;
|
29
36
|
} rbpcapjob_t;
|
30
37
|
|
@@ -68,7 +75,7 @@ rbpcap_s_lookupdev(VALUE self)
|
|
68
75
|
dev = pcap_lookupdev(eb);
|
69
76
|
if (dev == NULL) {
|
70
77
|
rb_raise(rb_eRuntimeError, "%s", eb);
|
71
|
-
|
78
|
+
}
|
72
79
|
ret_dev = rb_str_new2(dev);
|
73
80
|
#endif
|
74
81
|
return ret_dev;
|
@@ -104,11 +111,15 @@ static int rbpcap_ready(rbpcap_t *rbp) {
|
|
104
111
|
return 1;
|
105
112
|
}
|
106
113
|
|
107
|
-
static void
|
114
|
+
static void rbpcap_free(rbpcap_t *rbp) {
|
108
115
|
if (rbp->pd)
|
109
116
|
pcap_close(rbp->pd);
|
117
|
+
|
118
|
+
if (rbp->pdt)
|
119
|
+
pcap_dump_close(rbp->pdt);
|
110
120
|
|
111
121
|
rbp->pd = NULL;
|
122
|
+
rbp->pdt = NULL;
|
112
123
|
free(rbp);
|
113
124
|
}
|
114
125
|
|
@@ -119,7 +130,7 @@ rbpcap_new_s(VALUE class)
|
|
119
130
|
rbpcap_t *rbp;
|
120
131
|
|
121
132
|
// need to make destructor do a pcap_close later
|
122
|
-
self = Data_Make_Struct(class, rbpcap_t, 0,
|
133
|
+
self = Data_Make_Struct(class, rbpcap_t, 0, rbpcap_free, rbp);
|
123
134
|
rb_obj_call_init(self, 0, 0);
|
124
135
|
|
125
136
|
memset(rbp, 0, sizeof(rbpcap_t));
|
@@ -146,7 +157,7 @@ rbpcap_setfilter(VALUE self, VALUE filter)
|
|
146
157
|
if(pcap_lookupnet(rbp->iface, &netid, &mask, eb) < 0)
|
147
158
|
rb_raise(rb_eRuntimeError, "%s", eb);
|
148
159
|
|
149
|
-
if(pcap_compile(rbp->pd, &bpf,
|
160
|
+
if(pcap_compile(rbp->pd, &bpf, RSTRING_PTR(filter), 0, mask) < 0)
|
150
161
|
rb_raise(rb_eRuntimeError, "invalid bpf filter");
|
151
162
|
|
152
163
|
if(pcap_setfilter(rbp->pd, &bpf) < 0)
|
@@ -183,12 +194,18 @@ rbpcap_open_live(VALUE self, VALUE iface,VALUE snaplen,VALUE promisc, VALUE time
|
|
183
194
|
|
184
195
|
Data_Get_Struct(self, rbpcap_t, rbp);
|
185
196
|
|
197
|
+
|
186
198
|
rbp->type = LIVE;
|
187
199
|
memset(rbp->iface, 0, sizeof(rbp->iface));
|
188
|
-
strncpy(rbp->iface,
|
200
|
+
strncpy(rbp->iface, RSTRING_PTR(iface), sizeof(rbp->iface) - 1);
|
189
201
|
|
202
|
+
|
203
|
+
if(rbp->pd) {
|
204
|
+
pcap_close(rbp->pd);
|
205
|
+
}
|
206
|
+
|
190
207
|
rbp->pd = pcap_open_live(
|
191
|
-
|
208
|
+
RSTRING_PTR(iface),
|
192
209
|
NUM2INT(snaplen),
|
193
210
|
promisc_value,
|
194
211
|
NUM2INT(timeout),
|
@@ -223,7 +240,7 @@ rbpcap_open_offline(VALUE self, VALUE filename)
|
|
223
240
|
rbp->type = OFFLINE;
|
224
241
|
|
225
242
|
rbp->pd = pcap_open_offline(
|
226
|
-
|
243
|
+
RSTRING_PTR(filename),
|
227
244
|
eb
|
228
245
|
);
|
229
246
|
|
@@ -242,6 +259,85 @@ rbpcap_open_offline_s(VALUE class, VALUE filename)
|
|
242
259
|
return rbpcap_open_offline(iPcap, filename);
|
243
260
|
}
|
244
261
|
|
262
|
+
static VALUE
|
263
|
+
rbpcap_open_dead(VALUE self, VALUE linktype, VALUE snaplen)
|
264
|
+
{
|
265
|
+
rbpcap_t *rbp;
|
266
|
+
|
267
|
+
|
268
|
+
if(TYPE(linktype) != T_FIXNUM)
|
269
|
+
rb_raise(rb_eArgError, "linktype must be a fixnum");
|
270
|
+
if(TYPE(snaplen) != T_FIXNUM)
|
271
|
+
rb_raise(rb_eArgError, "snaplen must be a fixnum");
|
272
|
+
|
273
|
+
Data_Get_Struct(self, rbpcap_t, rbp);
|
274
|
+
|
275
|
+
memset(rbp->iface, 0, sizeof(rbp->iface));
|
276
|
+
rbp->type = OFFLINE;
|
277
|
+
|
278
|
+
rbp->pd = pcap_open_dead(
|
279
|
+
NUM2INT(linktype),
|
280
|
+
NUM2INT(snaplen)
|
281
|
+
);
|
282
|
+
|
283
|
+
return self;
|
284
|
+
}
|
285
|
+
|
286
|
+
static VALUE
|
287
|
+
rbpcap_open_dead_s(VALUE class, VALUE linktype, VALUE snaplen)
|
288
|
+
{
|
289
|
+
VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
|
290
|
+
|
291
|
+
return rbpcap_open_dead(iPcap, linktype, snaplen);
|
292
|
+
}
|
293
|
+
|
294
|
+
|
295
|
+
static VALUE
|
296
|
+
rbpcap_dump_open(VALUE self, VALUE filename)
|
297
|
+
{
|
298
|
+
rbpcap_t *rbp;
|
299
|
+
|
300
|
+
if(TYPE(filename) != T_STRING)
|
301
|
+
rb_raise(rb_eArgError, "filename must be a string");
|
302
|
+
|
303
|
+
Data_Get_Struct(self, rbpcap_t, rbp);
|
304
|
+
rbp->pdt = pcap_dump_open(
|
305
|
+
rbp->pd,
|
306
|
+
RSTRING_PTR(filename)
|
307
|
+
);
|
308
|
+
|
309
|
+
return self;
|
310
|
+
}
|
311
|
+
|
312
|
+
//not sure if this deviates too much from the way the rest of this class works?
|
313
|
+
static VALUE
|
314
|
+
rbpcap_dump(VALUE self, VALUE caplen, VALUE pktlen, VALUE packet)
|
315
|
+
{
|
316
|
+
rbpcap_t *rbp;
|
317
|
+
struct pcap_pkthdr pcap_hdr;
|
318
|
+
|
319
|
+
if(TYPE(packet) != T_STRING)
|
320
|
+
rb_raise(rb_eArgError, "packet data must be a string");
|
321
|
+
if(TYPE(caplen) != T_FIXNUM)
|
322
|
+
rb_raise(rb_eArgError, "caplen must be a fixnum");
|
323
|
+
if(TYPE(pktlen) != T_FIXNUM)
|
324
|
+
rb_raise(rb_eArgError, "pktlen must be a fixnum");
|
325
|
+
|
326
|
+
Data_Get_Struct(self, rbpcap_t, rbp);
|
327
|
+
|
328
|
+
gettimeofday(&pcap_hdr.ts, NULL);
|
329
|
+
pcap_hdr.caplen = NUM2UINT(caplen);
|
330
|
+
pcap_hdr.len = NUM2UINT(pktlen);
|
331
|
+
|
332
|
+
pcap_dump(
|
333
|
+
(u_char*)rbp->pdt,
|
334
|
+
&pcap_hdr,
|
335
|
+
(unsigned char *)RSTRING_PTR(packet)
|
336
|
+
);
|
337
|
+
|
338
|
+
return self;
|
339
|
+
}
|
340
|
+
|
245
341
|
static VALUE
|
246
342
|
rbpcap_inject(VALUE self, VALUE payload)
|
247
343
|
{
|
@@ -257,68 +353,65 @@ rbpcap_inject(VALUE self, VALUE payload)
|
|
257
353
|
/* WinPcap does not have a pcap_inject call we use pcap_sendpacket, if it suceedes
|
258
354
|
* we simply return the amount of packets request to inject, else we fail.
|
259
355
|
*/
|
260
|
-
if(pcap_sendpacket(rbp->pd,
|
356
|
+
if(pcap_sendpacket(rbp->pd, RSTRING_PTR(payload), RSTRING_LEN(payload)) != 0) {
|
261
357
|
rb_raise(rb_eRuntimeError, "%s", pcap_geterr(rbp->pd));
|
262
358
|
}
|
263
|
-
return INT2NUM(
|
359
|
+
return INT2NUM(RSTRING_LEN(payload));
|
264
360
|
#else
|
265
|
-
return INT2NUM(pcap_inject(rbp->pd,
|
361
|
+
return INT2NUM(pcap_inject(rbp->pd, RSTRING_PTR(payload), RSTRING_LEN(payload)));
|
266
362
|
#endif
|
267
363
|
}
|
268
364
|
|
269
365
|
|
270
366
|
static void rbpcap_handler(rbpcapjob_t *job, struct pcap_pkthdr *hdr, u_char *pkt){
|
271
|
-
job->pkt = pkt;
|
367
|
+
job->pkt = (unsigned char *)pkt;
|
272
368
|
job->hdr = *hdr;
|
273
369
|
}
|
274
370
|
|
275
371
|
static VALUE
|
276
372
|
rbpcap_next(VALUE self)
|
277
373
|
{
|
278
|
-
|
374
|
+
rbpcap_t *rbp;
|
279
375
|
rbpcapjob_t job;
|
280
376
|
char eb[PCAP_ERRBUF_SIZE];
|
281
377
|
int ret;
|
282
378
|
|
283
|
-
|
379
|
+
Data_Get_Struct(self, rbpcap_t, rbp);
|
284
380
|
if(! rbpcap_ready(rbp)) return self;
|
285
381
|
pcap_setnonblock(rbp->pd, 1, eb);
|
286
382
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
TRAP_END;
|
383
|
+
#ifndef RUBY_19
|
384
|
+
TRAP_BEG;
|
385
|
+
#endif
|
386
|
+
ret = pcap_dispatch(rbp->pd, 1, (pcap_handler) rbpcap_handler, (u_char *)&job);
|
387
|
+
#ifndef RUBY_19
|
388
|
+
TRAP_END;
|
389
|
+
#endif
|
295
390
|
|
296
|
-
if(rbp->type
|
391
|
+
if(rbp->type == OFFLINE && ret <= 0) return Qnil;
|
297
392
|
|
298
|
-
|
299
|
-
|
393
|
+
if(ret > 0 && job.hdr.caplen > 0)
|
394
|
+
return rb_str_new((char *) job.pkt, job.hdr.caplen);
|
300
395
|
|
301
|
-
|
396
|
+
return Qnil;
|
302
397
|
}
|
303
398
|
|
304
399
|
static VALUE
|
305
400
|
rbpcap_capture(VALUE self)
|
306
401
|
{
|
307
402
|
rbpcap_t *rbp;
|
308
|
-
|
403
|
+
int fno = -1;
|
404
|
+
|
309
405
|
Data_Get_Struct(self, rbpcap_t, rbp);
|
310
406
|
|
311
407
|
if(! rbpcap_ready(rbp)) return self;
|
312
408
|
|
313
|
-
|
409
|
+
fno = pcap_get_selectable_fd(rbp->pd);
|
314
410
|
|
411
|
+
for(;;) {
|
315
412
|
VALUE packet = rbpcap_next(self);
|
316
|
-
|
317
|
-
|
318
|
-
break;
|
319
|
-
|
320
|
-
if(packet != Qnil)
|
321
|
-
rb_yield(packet);
|
413
|
+
if(packet == Qnil && rbp->type == OFFLINE) break;
|
414
|
+
packet == Qnil ? rb_thread_wait_fd(fno) : rb_yield(packet);
|
322
415
|
}
|
323
416
|
|
324
417
|
return self;
|
@@ -349,6 +442,7 @@ rbpcap_snapshot(VALUE self)
|
|
349
442
|
return INT2NUM(pcap_snapshot(rbp->pd));
|
350
443
|
}
|
351
444
|
|
445
|
+
//returns a hash
|
352
446
|
static VALUE
|
353
447
|
rbpcap_stats(VALUE self)
|
354
448
|
{
|
@@ -373,10 +467,27 @@ rbpcap_stats(VALUE self)
|
|
373
467
|
void
|
374
468
|
Init_pcaprub()
|
375
469
|
{
|
376
|
-
|
470
|
+
/*
|
471
|
+
* Document-class: Pcap
|
472
|
+
*
|
473
|
+
* Main class defined by the pcaprub extension.
|
474
|
+
*/
|
377
475
|
rb_cPcap = rb_define_class("Pcap", rb_cObject);
|
476
|
+
|
477
|
+
/*
|
478
|
+
* Document-module-function: Version
|
479
|
+
* return the version of Pcaprub extension
|
480
|
+
*/
|
378
481
|
rb_define_module_function(rb_cPcap, "version", rbpcap_s_version, 0);
|
482
|
+
|
483
|
+
/*
|
484
|
+
* return the device id
|
485
|
+
*/
|
379
486
|
rb_define_module_function(rb_cPcap, "lookupdev", rbpcap_s_lookupdev, 0);
|
487
|
+
|
488
|
+
/*
|
489
|
+
* return the net
|
490
|
+
*/
|
380
491
|
rb_define_module_function(rb_cPcap, "lookupnet", rbpcap_s_lookupnet, 1);
|
381
492
|
|
382
493
|
rb_define_const(rb_cPcap, "DLT_NULL", INT2NUM(DLT_NULL));
|
@@ -405,15 +516,52 @@ Init_pcaprub()
|
|
405
516
|
|
406
517
|
rb_define_singleton_method(rb_cPcap, "open_live", rbpcap_open_live_s, 4);
|
407
518
|
rb_define_singleton_method(rb_cPcap, "open_offline", rbpcap_open_offline_s, 1);
|
408
|
-
|
519
|
+
rb_define_singleton_method(rb_cPcap, "open_dead", rbpcap_open_dead_s, 2);
|
520
|
+
rb_define_singleton_method(rb_cPcap, "dump_open", rbpcap_dump_open, 1);
|
521
|
+
|
522
|
+
|
523
|
+
/*
|
524
|
+
* Document-method: dump
|
525
|
+
*/
|
526
|
+
rb_define_method(rb_cPcap, "dump", rbpcap_dump, 3);
|
527
|
+
|
528
|
+
/*
|
529
|
+
* Document-method: each
|
530
|
+
*/
|
409
531
|
rb_define_method(rb_cPcap, "each", rbpcap_capture, 0);
|
532
|
+
|
533
|
+
/*
|
534
|
+
* Document-method: next
|
535
|
+
*/
|
410
536
|
rb_define_method(rb_cPcap, "next", rbpcap_next, 0);
|
537
|
+
|
538
|
+
/*
|
539
|
+
* Document-method: setfilter
|
540
|
+
*/
|
411
541
|
rb_define_method(rb_cPcap, "setfilter", rbpcap_setfilter, 1);
|
542
|
+
|
543
|
+
/*
|
544
|
+
* Document-method: inject
|
545
|
+
*/
|
412
546
|
rb_define_method(rb_cPcap, "inject", rbpcap_inject, 1);
|
547
|
+
|
548
|
+
/*
|
549
|
+
* Document-method: datalink
|
550
|
+
*/
|
413
551
|
rb_define_method(rb_cPcap, "datalink", rbpcap_datalink, 0);
|
414
552
|
|
553
|
+
/*
|
554
|
+
* Document-method: snapshot
|
555
|
+
*/
|
415
556
|
rb_define_method(rb_cPcap, "snapshot", rbpcap_snapshot, 0);
|
557
|
+
|
558
|
+
/*
|
559
|
+
* Document-method: snaplen
|
560
|
+
*/
|
416
561
|
rb_define_method(rb_cPcap, "snaplen", rbpcap_snapshot, 0);
|
562
|
+
|
563
|
+
/*
|
564
|
+
* Document-method: stats
|
565
|
+
*/
|
417
566
|
rb_define_method(rb_cPcap, "stats", rbpcap_stats, 0);
|
418
567
|
}
|
419
|
-
|
data/pcaprub.gemspec
CHANGED
data/test/test_pcaprub_unit.rb
CHANGED
@@ -81,15 +81,24 @@ class Pcap::UnitTest < Test::Unit::TestCase
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def test_pcap_next
|
84
|
-
=begin
|
85
84
|
d = Pcap.lookupdev
|
86
85
|
o = Pcap.open_live(d, 1344, true, 1)
|
86
|
+
|
87
87
|
@c = 0
|
88
|
-
t = Thread.new { while(true); @c += 1; end; }
|
89
|
-
|
88
|
+
t = Thread.new { while(true); @c += 1; select(nil, nil, nil, 0.10); end; }
|
89
|
+
|
90
|
+
require 'timeout'
|
91
|
+
begin
|
92
|
+
Timeout.timeout(10) do
|
93
|
+
o.each do |pkt|
|
94
|
+
end
|
95
|
+
end
|
96
|
+
rescue ::Timeout::Error
|
97
|
+
end
|
98
|
+
|
90
99
|
t.kill
|
91
|
-
|
100
|
+
puts "Background thread ticked #{@c} times while capture was running"
|
92
101
|
true
|
93
102
|
end
|
94
103
|
|
95
|
-
|
104
|
+
end
|