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.
@@ -10,17 +10,31 @@ PacketRub project (http://packetrub.rubyforge.org).
10
10
  Requirements:
11
11
  libpcap - http://www.tcpdump.org
12
12
 
13
- == Other Repositories
13
+ == Installation
14
14
 
15
- The latest community svn version can be obtained from Subversion:
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
- (credits: github.com/dxoigmn/pcaprub)
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
@@ -80,5 +80,7 @@ Rake::RDocTask.new do |rdoc|
80
80
  rdoc.rdoc_dir = 'rdoc'
81
81
  rdoc.title = "pcaprub #{version}"
82
82
  rdoc.rdoc_files.include('README*')
83
+ rdoc.rdoc_files.include('LICENSE*')
83
84
  rdoc.rdoc_files.include('lib/**/*.rb')
85
+ rdoc.rdoc_files.include('ext/**/*.c')
84
86
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.1
1
+ 0.9.0
@@ -15,5 +15,12 @@ else
15
15
  have_library("pcap", "pcap_setnonblock")
16
16
  end
17
17
 
18
+ if ( RUBY_VERSION =~ /^1\.9/ )
19
+ $CFLAGS += " -DRUBY_19"
20
+ end
21
+
22
+
18
23
  dir_config(extension_name)
19
24
  create_makefile(extension_name)
25
+
26
+
@@ -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.8-dev"
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 rbpcap_close(rbpcap_t *rbp) {
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, rbpcap_close, rbp);
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, RSTRING(filter)->ptr, 0, mask) < 0)
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, RSTRING(iface)->ptr, sizeof(rbp->iface) - 1);
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
- RSTRING(iface)->ptr,
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
- RSTRING(filename)->ptr,
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, RSTRING(payload)->ptr, RSTRING(payload)->len) != 0) {
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(RSTRING(payload)->len);
359
+ return INT2NUM(RSTRING_LEN(payload));
264
360
  #else
265
- return INT2NUM(pcap_inject(rbp->pd, RSTRING(payload)->ptr, RSTRING(payload)->len));
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
- rbpcap_t *rbp;
374
+ rbpcap_t *rbp;
279
375
  rbpcapjob_t job;
280
376
  char eb[PCAP_ERRBUF_SIZE];
281
377
  int ret;
282
378
 
283
- Data_Get_Struct(self, rbpcap_t, rbp);
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
- TRAP_BEG;
288
-
289
- while(! (ret = pcap_dispatch(rbp->pd, 1, (pcap_handler) rbpcap_handler, (u_char *)&job))) {
290
- if(rbp->type == OFFLINE) break;
291
- rb_thread_schedule();
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 = OFFLINE && ret <= 0) return Qnil;
391
+ if(rbp->type == OFFLINE && ret <= 0) return Qnil;
297
392
 
298
- if(job.hdr.caplen > 0)
299
- return rb_str_new(job.pkt, job.hdr.caplen);
393
+ if(ret > 0 && job.hdr.caplen > 0)
394
+ return rb_str_new((char *) job.pkt, job.hdr.caplen);
300
395
 
301
- return Qnil;
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
- for(;;) {
409
+ fno = pcap_get_selectable_fd(rbp->pd);
314
410
 
411
+ for(;;) {
315
412
  VALUE packet = rbpcap_next(self);
316
-
317
- if(packet == Qnil && rbp->type == OFFLINE)
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
- // Pcap
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
-
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{pcaprub}
8
- s.version = "0.8.1"
8
+ s.version = "0.9.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["shadowbq"]
@@ -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
- x = o.next
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
- =end
100
+ puts "Background thread ticked #{@c} times while capture was running"
92
101
  true
93
102
  end
94
103
 
95
- enD
104
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pcaprub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - shadowbq