pcaprub 0.6.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,3 +9,18 @@ PacketRub project (http://packetrub.rubyforge.org).
9
9
 
10
10
  Requirements:
11
11
  libpcap - http://www.tcpdump.org
12
+
13
+ == Other Repositories
14
+
15
+ The latest community svn version can be obtained from Subversion:
16
+ svn checkout http://pcaprub.rubyforge.org/svn/trunk/
17
+
18
+ The Metasploit Project also provides a Subversion repository:
19
+ svn checkout http://metasploit.com/svn/framework3/trunk/external/pcaprub/
20
+
21
+ (credits: github.com/dxoigmn/pcaprub)
22
+
23
+ == Current Repository for Gemcutter source
24
+
25
+ The Git Repo on Github @shadowbq is forked from the Metasploit SVN repo
26
+ git clone git://github.com/shadowbq/pcaprub.git
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.2
1
+ 0.8.0
@@ -1,7 +1,19 @@
1
1
  require 'mkmf'
2
-
3
2
  extension_name = 'pcaprub'
4
- have_library("pcap", "pcap_open_live")
3
+
4
+ if /i386-mswin32/ =~ RUBY_PLATFORM
5
+ pcap_dir = with_config("pcap-dir", "C:\WpdPack")
6
+ pcap_includedir = with_config("pcap-includedir", pcap_dir + "\\include")
7
+ pcap_libdir = with_config("pcap-libdir", pcap_dir + "\\lib")
8
+
9
+ $CFLAGS = "-DWIN32 -I#{pcap_includedir}"
10
+ $LDFLAGS = "/link /LIBPATH:#{pcap_libdir}"
11
+ have_library("wpcap", "pcap_open_live")
12
+ have_library("wpcap", "pcap_setnonblock")
13
+ else
14
+ have_library("pcap", "pcap_open_live")
15
+ have_library("pcap", "pcap_setnonblock")
16
+ end
17
+
5
18
  dir_config(extension_name)
6
- #$CPPFLAGS += " -DRUBY_19" if RUBY_VERSION =~ /1.9/
7
19
  create_makefile(extension_name)
@@ -22,3 +22,27 @@ checked program was:
22
22
 
23
23
  --------------------
24
24
 
25
+ have_library: checking for pcap_setnonblock() in -lpcap... -------------------- yes
26
+
27
+ "cc -o conftest -I. -I/usr/local/lib/ruby/1.8/i386-freebsd7 -I. -O2 -fno-strict-aliasing -pipe -fPIC conftest.c -L. -L/usr/local/lib -Wl,-R/usr/local/lib -L. -rdynamic -lpcap -lruby18-static -lpcap -lpcap -lcrypt -lm -rpath=/usr/lib:/usr/local/lib -pthread -lc"
28
+ conftest.c: In function 't':
29
+ conftest.c:3: error: 'pcap_setnonblock' undeclared (first use in this function)
30
+ conftest.c:3: error: (Each undeclared identifier is reported only once
31
+ conftest.c:3: error: for each function it appears in.)
32
+ checked program was:
33
+ /* begin */
34
+ 1: /*top*/
35
+ 2: int main() { return 0; }
36
+ 3: int t() { void ((*volatile p)()); p = (void ((*)()))pcap_setnonblock; return 0; }
37
+ /* end */
38
+
39
+ "cc -o conftest -I. -I/usr/local/lib/ruby/1.8/i386-freebsd7 -I. -O2 -fno-strict-aliasing -pipe -fPIC conftest.c -L. -L/usr/local/lib -Wl,-R/usr/local/lib -L. -rdynamic -lpcap -lruby18-static -lpcap -lpcap -lcrypt -lm -rpath=/usr/lib:/usr/local/lib -pthread -lc"
40
+ checked program was:
41
+ /* begin */
42
+ 1: /*top*/
43
+ 2: int main() { return 0; }
44
+ 3: int t() { pcap_setnonblock(); return 0; }
45
+ /* end */
46
+
47
+ --------------------
48
+
@@ -3,8 +3,15 @@
3
3
 
4
4
  #include <pcap.h>
5
5
 
6
+ #if !defined(WIN32)
7
+ #include <netinet/in.h>
8
+ #include <arpa/inet.h>
9
+ #endif
10
+
6
11
  static VALUE rb_cPcap;
7
12
 
13
+ #define PCAPRUB_VERSION "0.8-dev"
14
+
8
15
  #define OFFLINE 1
9
16
  #define LIVE 2
10
17
 
@@ -14,6 +21,97 @@ typedef struct rbpcap {
14
21
  char type;
15
22
  } rbpcap_t;
16
23
 
24
+
25
+ typedef struct rbpcapjob {
26
+ struct pcap_pkthdr hdr;
27
+ char *pkt;
28
+ int wtf;
29
+ } rbpcapjob_t;
30
+
31
+ static VALUE
32
+ rbpcap_s_version(VALUE class)
33
+ {
34
+ return rb_str_new2(PCAPRUB_VERSION);
35
+ }
36
+
37
+
38
+ static VALUE
39
+ rbpcap_s_lookupdev(VALUE self)
40
+ {
41
+ char *dev = NULL;
42
+ char eb[PCAP_ERRBUF_SIZE];
43
+ VALUE ret_dev; /* device string to return */
44
+ #if defined(WIN32) /* pcap_lookupdev is broken on windows */
45
+ pcap_if_t *alldevs;
46
+ pcap_if_t *d;
47
+
48
+ /* Retrieve the device list from the local machine */
49
+ if (pcap_findalldevs(&alldevs,eb) == -1) {
50
+ rb_raise(rb_eRuntimeError,"%s",eb);
51
+ }
52
+
53
+ /* Find the first interface with an address and not loopback */
54
+ for(d = alldevs; d != NULL; d= d->next) {
55
+ if(d->name && d->addresses && !(d->flags & PCAP_IF_LOOPBACK)) {
56
+ dev=d->name;
57
+ break;
58
+ }
59
+ }
60
+
61
+ if (dev == NULL) {
62
+ rb_raise(rb_eRuntimeError,"%s","No valid interfaces found, Make sure WinPcap is installed.\n");
63
+ }
64
+ ret_dev = rb_str_new2(dev);
65
+ /* We don't need any more the device list. Free it */
66
+ pcap_freealldevs(alldevs);
67
+ #else
68
+ dev = pcap_lookupdev(eb);
69
+ if (dev == NULL) {
70
+ rb_raise(rb_eRuntimeError, "%s", eb);
71
+ }
72
+ ret_dev = rb_str_new2(dev);
73
+ #endif
74
+ return ret_dev;
75
+ }
76
+
77
+ static VALUE
78
+ rbpcap_s_lookupnet(VALUE self, VALUE dev)
79
+ {
80
+ bpf_u_int32 net, mask, m;
81
+ struct in_addr addr;
82
+ char eb[PCAP_ERRBUF_SIZE];
83
+ VALUE list;
84
+
85
+ Check_Type(dev, T_STRING);
86
+ if (pcap_lookupnet(STR2CSTR(dev), &net, &mask, eb) == -1) {
87
+ rb_raise(rb_eRuntimeError, "%s", eb);
88
+ }
89
+
90
+ addr.s_addr = net;
91
+ m = ntohl(mask);
92
+ list = rb_ary_new();
93
+ rb_ary_push(list, rb_str_new2((char *) inet_ntoa(addr)));
94
+ rb_ary_push(list, UINT2NUM(m));
95
+ return(list);
96
+ }
97
+
98
+
99
+ static int rbpcap_ready(rbpcap_t *rbp) {
100
+ if(! rbp->pd) {
101
+ rb_raise(rb_eArgError, "a device or pcap file must be opened first");
102
+ return 0;
103
+ }
104
+ return 1;
105
+ }
106
+
107
+ static void rbpcap_close(rbpcap_t *rbp) {
108
+ if (rbp->pd)
109
+ pcap_close(rbp->pd);
110
+
111
+ rbp->pd = NULL;
112
+ free(rbp);
113
+ }
114
+
17
115
  static VALUE
18
116
  rbpcap_new_s(VALUE class)
19
117
  {
@@ -21,101 +119,103 @@ rbpcap_new_s(VALUE class)
21
119
  rbpcap_t *rbp;
22
120
 
23
121
  // need to make destructor do a pcap_close later
24
- self = Data_Make_Struct(class, rbpcap_t, 0, free, rbp);
122
+ self = Data_Make_Struct(class, rbpcap_t, 0, rbpcap_close, rbp);
25
123
  rb_obj_call_init(self, 0, 0);
26
124
 
27
125
  memset(rbp, 0, sizeof(rbpcap_t));
28
-
126
+
29
127
  return self;
30
128
  }
31
129
 
32
130
  static VALUE
33
131
  rbpcap_setfilter(VALUE self, VALUE filter)
34
132
  {
35
- char eb[1024];
133
+ char eb[PCAP_ERRBUF_SIZE];
36
134
  rbpcap_t *rbp;
37
135
  u_int32_t mask = 0, netid = 0;
38
136
  struct bpf_program bpf;
39
137
 
40
138
  Data_Get_Struct(self, rbpcap_t, rbp);
41
139
 
42
- if(TYPE(filter) != T_STRING)
43
- rb_raise(rb_eArgError, "filter must be a string");
140
+ if(TYPE(filter) != T_STRING)
141
+ rb_raise(rb_eArgError, "filter must be a string");
44
142
 
143
+ if(! rbpcap_ready(rbp)) return self;
144
+
45
145
  if(rbp->type == LIVE)
46
- if(pcap_lookupnet(rbp->iface, &netid, &mask, eb) < 0)
47
- rb_raise(rb_eRuntimeError, "%s", eb);
146
+ if(pcap_lookupnet(rbp->iface, &netid, &mask, eb) < 0)
147
+ rb_raise(rb_eRuntimeError, "%s", eb);
48
148
 
49
149
  if(pcap_compile(rbp->pd, &bpf, RSTRING(filter)->ptr, 0, mask) < 0)
50
- rb_raise(rb_eRuntimeError, "invalid bpf filter");
150
+ rb_raise(rb_eRuntimeError, "invalid bpf filter");
51
151
 
52
152
  if(pcap_setfilter(rbp->pd, &bpf) < 0)
53
- rb_raise(rb_eRuntimeError, "unable to set bpf filter");
153
+ rb_raise(rb_eRuntimeError, "unable to set bpf filter");
54
154
 
55
155
  return self;
56
156
  }
57
157
 
158
+
58
159
  static VALUE
59
- rbpcap_open_live(VALUE self, VALUE interface, VALUE snaplen, VALUE promisc, VALUE timeout)
160
+ rbpcap_open_live(VALUE self, VALUE iface,VALUE snaplen,VALUE promisc, VALUE timeout)
60
161
  {
61
- char eb[1024];
162
+ char eb[PCAP_ERRBUF_SIZE];
62
163
  rbpcap_t *rbp;
63
164
  int promisc_value = 0;
64
165
 
65
- if(TYPE(interface) != T_STRING)
66
- rb_raise(rb_eArgError, "interface must be a string");
67
- if(TYPE(snaplen) != T_FIXNUM)
68
- rb_raise(rb_eArgError, "snaplen must be a fixnum");
69
- if(TYPE(timeout) != T_FIXNUM)
70
- rb_raise(rb_eArgError, "timeout must be a fixnum");
166
+ if(TYPE(iface) != T_STRING)
167
+ rb_raise(rb_eArgError, "interface must be a string");
168
+ if(TYPE(snaplen) != T_FIXNUM)
169
+ rb_raise(rb_eArgError, "snaplen must be a fixnum");
170
+ if(TYPE(timeout) != T_FIXNUM)
171
+ rb_raise(rb_eArgError, "timeout must be a fixnum");
71
172
 
72
173
  switch(promisc) {
73
- case Qtrue:
74
- promisc_value = 1;
75
- break;
76
- case Qfalse:
77
- promisc_value = 0;
78
- break;
79
- default:
80
- rb_raise(rb_eTypeError, "Argument not boolean");
174
+ case Qtrue:
175
+ promisc_value = 1;
176
+ break;
177
+ case Qfalse:
178
+ promisc_value = 0;
179
+ break;
180
+ default:
181
+ rb_raise(rb_eTypeError, "Argument not boolean");
81
182
  }
82
183
 
83
184
  Data_Get_Struct(self, rbpcap_t, rbp);
84
185
 
85
186
  rbp->type = LIVE;
86
187
  memset(rbp->iface, 0, sizeof(rbp->iface));
87
- strncpy(rbp->iface, RSTRING(interface)->ptr, sizeof(rbp->iface) - 1);
188
+ strncpy(rbp->iface, RSTRING(iface)->ptr, sizeof(rbp->iface) - 1);
88
189
 
89
190
  rbp->pd = pcap_open_live(
90
- RSTRING(interface)->ptr,
91
- NUM2INT(snaplen),
92
- promisc_value,
93
- NUM2INT(timeout),
94
- eb
191
+ RSTRING(iface)->ptr,
192
+ NUM2INT(snaplen),
193
+ promisc_value,
194
+ NUM2INT(timeout),
195
+ eb
95
196
  );
96
197
 
97
198
  if(!rbp->pd)
98
- rb_raise(rb_eRuntimeError, "%s", eb);
199
+ rb_raise(rb_eRuntimeError, "%s", eb);
99
200
 
100
201
  return self;
101
202
  }
102
203
 
103
204
  static VALUE
104
- rbpcap_open_live_s(VALUE class, VALUE interface, VALUE snaplen, VALUE promisc, VALUE timeout)
205
+ rbpcap_open_live_s(VALUE class, VALUE iface, VALUE snaplen, VALUE promisc, VALUE timeout)
105
206
  {
106
207
  VALUE iPcap = rb_funcall(rb_cPcap, rb_intern("new"), 0);
107
-
108
- return rbpcap_open_live(iPcap, interface, snaplen, promisc, timeout);
208
+ return rbpcap_open_live(iPcap, iface, snaplen, promisc, timeout);
109
209
  }
110
210
 
111
211
  static VALUE
112
212
  rbpcap_open_offline(VALUE self, VALUE filename)
113
213
  {
114
- char eb[1024];
214
+ char eb[PCAP_ERRBUF_SIZE];
115
215
  rbpcap_t *rbp;
116
216
 
117
- if(TYPE(filename) != T_STRING)
118
- rb_raise(rb_eArgError, "filename must be a string");
217
+ if(TYPE(filename) != T_STRING)
218
+ rb_raise(rb_eArgError, "filename must be a string");
119
219
 
120
220
  Data_Get_Struct(self, rbpcap_t, rbp);
121
221
 
@@ -123,12 +223,12 @@ rbpcap_open_offline(VALUE self, VALUE filename)
123
223
  rbp->type = OFFLINE;
124
224
 
125
225
  rbp->pd = pcap_open_offline(
126
- RSTRING(filename)->ptr,
127
- eb
226
+ RSTRING(filename)->ptr,
227
+ eb
128
228
  );
129
229
 
130
230
  if(!rbp->pd)
131
- rb_raise(rb_eRuntimeError, "%s", eb);
231
+ rb_raise(rb_eRuntimeError, "%s", eb);
132
232
 
133
233
  return self;
134
234
  }
@@ -147,32 +247,56 @@ rbpcap_inject(VALUE self, VALUE payload)
147
247
  {
148
248
  rbpcap_t *rbp;
149
249
 
150
- if(TYPE(payload) != T_STRING)
151
- rb_raise(rb_eArgError, "payload must be a string");
250
+ if(TYPE(payload) != T_STRING)
251
+ rb_raise(rb_eArgError, "payload must be a string");
152
252
 
153
253
  Data_Get_Struct(self, rbpcap_t, rbp);
154
254
 
255
+ if(! rbpcap_ready(rbp)) return self;
256
+ #if defined(WIN32)
257
+ /* WinPcap does not have a pcap_inject call we use pcap_sendpacket, if it suceedes
258
+ * we simply return the amount of packets request to inject, else we fail.
259
+ */
260
+ if(pcap_sendpacket(rbp->pd, RSTRING(payload)->ptr, RSTRING(payload)->len) != 0) {
261
+ rb_raise(rb_eRuntimeError, "%s", pcap_geterr(rbp->pd));
262
+ }
263
+ return INT2NUM(RSTRING(payload)->len);
264
+ #else
155
265
  return INT2NUM(pcap_inject(rbp->pd, RSTRING(payload)->ptr, RSTRING(payload)->len));
266
+ #endif
267
+ }
268
+
269
+
270
+ static void rbpcap_handler(rbpcapjob_t *job, struct pcap_pkthdr *hdr, u_char *pkt){
271
+ job->pkt = pkt;
272
+ job->hdr = *hdr;
156
273
  }
157
274
 
158
275
  static VALUE
159
276
  rbpcap_next(VALUE self)
160
277
  {
161
- int ret;
162
- char *pkt;
163
278
  rbpcap_t *rbp;
164
- struct pcap_pkthdr pkthdr;
165
-
279
+ rbpcapjob_t job;
280
+ char eb[PCAP_ERRBUF_SIZE];
281
+ int ret;
282
+
166
283
  Data_Get_Struct(self, rbpcap_t, rbp);
167
-
168
- memset(&pkthdr, 0, sizeof(pkthdr));
284
+ if(! rbpcap_ready(rbp)) return self;
285
+ pcap_setnonblock(rbp->pd, 1, eb);
169
286
 
170
287
  TRAP_BEG;
171
- pkt = (char *)pcap_next(rbp->pd, &pkthdr);
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
+
172
294
  TRAP_END;
173
295
 
174
- if(pkthdr.caplen > 0)
175
- return rb_str_new(pkt, pkthdr.caplen);
296
+ if(rbp->type = OFFLINE && ret <= 0) return Qnil;
297
+
298
+ if(job.hdr.caplen > 0)
299
+ return rb_str_new(job.pkt, job.hdr.caplen);
176
300
 
177
301
  return Qnil;
178
302
  }
@@ -180,22 +304,21 @@ rbpcap_next(VALUE self)
180
304
  static VALUE
181
305
  rbpcap_capture(VALUE self)
182
306
  {
183
- int ret;
184
- char *pkt;
185
307
  rbpcap_t *rbp;
186
- struct pcap_pkthdr pkthdr;
187
308
 
188
309
  Data_Get_Struct(self, rbpcap_t, rbp);
189
310
 
311
+ if(! rbpcap_ready(rbp)) return self;
312
+
190
313
  for(;;) {
191
314
 
192
- VALUE packet = rbpcap_next(self);
315
+ VALUE packet = rbpcap_next(self);
193
316
 
194
- if(packet == Qnil && rbp->type == OFFLINE)
195
- break;
317
+ if(packet == Qnil && rbp->type == OFFLINE)
318
+ break;
196
319
 
197
- if(packet != Qnil)
198
- rb_yield(packet);
320
+ if(packet != Qnil)
321
+ rb_yield(packet);
199
322
  }
200
323
 
201
324
  return self;
@@ -208,16 +331,54 @@ rbpcap_datalink(VALUE self)
208
331
  rbpcap_t *rbp;
209
332
 
210
333
  Data_Get_Struct(self, rbpcap_t, rbp);
211
-
334
+
335
+ if(! rbpcap_ready(rbp)) return self;
336
+
212
337
  return INT2NUM(pcap_datalink(rbp->pd));
213
338
  }
214
339
 
340
+ static VALUE
341
+ rbpcap_snapshot(VALUE self)
342
+ {
343
+ rbpcap_t *rbp;
344
+
345
+ Data_Get_Struct(self, rbpcap_t, rbp);
346
+
347
+ if(! rbpcap_ready(rbp)) return self;
348
+
349
+ return INT2NUM(pcap_snapshot(rbp->pd));
350
+ }
351
+
352
+ static VALUE
353
+ rbpcap_stats(VALUE self)
354
+ {
355
+ rbpcap_t *rbp;
356
+ struct pcap_stat stat;
357
+ VALUE hash;
358
+
359
+ Data_Get_Struct(self, rbpcap_t, rbp);
360
+
361
+ if(! rbpcap_ready(rbp)) return self;
362
+
363
+ if (pcap_stats(rbp->pd, &stat) == -1)
364
+ return Qnil;
365
+
366
+ hash = rb_hash_new();
367
+ rb_hash_aset(hash, rb_str_new2("recv"), UINT2NUM(stat.ps_recv));
368
+ rb_hash_aset(hash, rb_str_new2("drop"), UINT2NUM(stat.ps_drop));
369
+ rb_hash_aset(hash, rb_str_new2("idrop"), UINT2NUM(stat.ps_ifdrop));
370
+ return hash;
371
+ }
372
+
215
373
  void
216
374
  Init_pcaprub()
217
375
  {
218
376
  // Pcap
219
377
  rb_cPcap = rb_define_class("Pcap", rb_cObject);
220
-
378
+ rb_define_module_function(rb_cPcap, "version", rbpcap_s_version, 0);
379
+ rb_define_module_function(rb_cPcap, "lookupdev", rbpcap_s_lookupdev, 0);
380
+ rb_define_module_function(rb_cPcap, "lookupnet", rbpcap_s_lookupnet, 1);
381
+
221
382
  rb_define_const(rb_cPcap, "DLT_NULL", INT2NUM(DLT_NULL));
222
383
  rb_define_const(rb_cPcap, "DLT_EN10MB", INT2NUM(DLT_EN10MB));
223
384
  rb_define_const(rb_cPcap, "DLT_EN3MB", INT2NUM(DLT_EN3MB));
@@ -230,7 +391,15 @@ Init_pcaprub()
230
391
  rb_define_const(rb_cPcap, "DLT_PPP", INT2NUM(DLT_PPP));
231
392
  rb_define_const(rb_cPcap, "DLT_FDDI", INT2NUM(DLT_FDDI));
232
393
  rb_define_const(rb_cPcap, "DLT_ATM_RFC1483", INT2NUM(DLT_ATM_RFC1483));
233
-
394
+ rb_define_const(rb_cPcap, "DLT_RAW", INT2NUM(DLT_RAW));
395
+ rb_define_const(rb_cPcap, "DLT_SLIP_BSDOS", INT2NUM(DLT_SLIP_BSDOS));
396
+ rb_define_const(rb_cPcap, "DLT_PPP_BSDOS", INT2NUM(DLT_PPP_BSDOS));
397
+ rb_define_const(rb_cPcap, "DLT_IEEE802_11", INT2NUM(DLT_IEEE802_11));
398
+ rb_define_const(rb_cPcap, "DLT_IEEE802_11_RADIO", INT2NUM(DLT_IEEE802_11_RADIO));
399
+ rb_define_const(rb_cPcap, "DLT_IEEE802_11_RADIO_AVS", INT2NUM(DLT_IEEE802_11_RADIO_AVS));
400
+ rb_define_const(rb_cPcap, "DLT_LINUX_SLL", INT2NUM(DLT_LINUX_SLL));
401
+ rb_define_const(rb_cPcap, "DLT_PRISM_HEADER", INT2NUM(DLT_PRISM_HEADER));
402
+ rb_define_const(rb_cPcap, "DLT_AIRONET_HEADER", INT2NUM(DLT_AIRONET_HEADER));
234
403
 
235
404
  rb_define_singleton_method(rb_cPcap, "new", rbpcap_new_s, 0);
236
405
 
@@ -242,5 +411,9 @@ Init_pcaprub()
242
411
  rb_define_method(rb_cPcap, "setfilter", rbpcap_setfilter, 1);
243
412
  rb_define_method(rb_cPcap, "inject", rbpcap_inject, 1);
244
413
  rb_define_method(rb_cPcap, "datalink", rbpcap_datalink, 0);
245
-
414
+
415
+ rb_define_method(rb_cPcap, "snapshot", rbpcap_snapshot, 0);
416
+ rb_define_method(rb_cPcap, "snaplen", rbpcap_snapshot, 0);
417
+ rb_define_method(rb_cPcap, "stats", rbpcap_stats, 0);
246
418
  }
419
+
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{pcaprub}
8
- s.version = "0.6.2"
8
+ s.version = "0.8.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"]
12
- s.date = %q{2010-02-06}
12
+ s.date = %q{2010-02-08}
13
13
  s.description = %q{libpcap bindings for ruby}
14
14
  s.email = %q{shadowbq@gmail.com}
15
15
  s.extensions = ["ext/pcaprub/extconf.rb"]
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.6.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - shadowbq
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-06 00:00:00 -05:00
12
+ date: 2010-02-08 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15