packetfu 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +3 -0
  2. data/INSTALL.rdoc +40 -0
  3. data/LICENSE.txt +25 -0
  4. data/examples/100kpackets.rb +41 -0
  5. data/examples/ackscan.rb +38 -0
  6. data/examples/arp.rb +60 -0
  7. data/examples/arphood.rb +59 -0
  8. data/examples/dissect_thinger.rb +22 -0
  9. data/examples/ethernet.rb +10 -0
  10. data/examples/examples.rb +3 -0
  11. data/examples/ids.rb +4 -0
  12. data/examples/idsv2.rb +6 -0
  13. data/examples/new-simple-stats.rb +52 -0
  14. data/examples/oui.txt +84177 -0
  15. data/examples/packetfu-shell.rb +113 -0
  16. data/examples/simple-sniffer.rb +40 -0
  17. data/examples/simple-stats.rb +50 -0
  18. data/examples/slammer.rb +33 -0
  19. data/examples/uniqpcap.rb +15 -0
  20. data/lib/packetfu.rb +147 -0
  21. data/lib/packetfu/capture.rb +169 -0
  22. data/lib/packetfu/config.rb +58 -0
  23. data/lib/packetfu/inject.rb +65 -0
  24. data/lib/packetfu/packet.rb +533 -0
  25. data/lib/packetfu/pcap.rb +594 -0
  26. data/lib/packetfu/protos/arp.rb +268 -0
  27. data/lib/packetfu/protos/eth.rb +296 -0
  28. data/lib/packetfu/protos/hsrp.rb +206 -0
  29. data/lib/packetfu/protos/icmp.rb +179 -0
  30. data/lib/packetfu/protos/invalid.rb +55 -0
  31. data/lib/packetfu/protos/ip.rb +378 -0
  32. data/lib/packetfu/protos/ipv6.rb +250 -0
  33. data/lib/packetfu/protos/tcp.rb +1127 -0
  34. data/lib/packetfu/protos/udp.rb +240 -0
  35. data/lib/packetfu/structfu.rb +294 -0
  36. data/lib/packetfu/utils.rb +194 -0
  37. data/lib/packetfu/version.rb +50 -0
  38. data/packetfu.gemspec +21 -0
  39. data/setup.rb +1586 -0
  40. data/test/all_tests.rb +41 -0
  41. data/test/ethpacket_spec.rb +74 -0
  42. data/test/packet_spec.rb +73 -0
  43. data/test/packet_subclasses_spec.rb +13 -0
  44. data/test/packetfu_spec.rb +90 -0
  45. data/test/ptest.rb +16 -0
  46. data/test/sample-ipv6.pcap +0 -0
  47. data/test/sample.pcap +0 -0
  48. data/test/sample2.pcap +0 -0
  49. data/test/sample_hsrp_pcapr.cap +0 -0
  50. data/test/structfu_spec.rb +335 -0
  51. data/test/tcp_spec.rb +101 -0
  52. data/test/test_arp.rb +135 -0
  53. data/test/test_eth.rb +91 -0
  54. data/test/test_hsrp.rb +20 -0
  55. data/test/test_icmp.rb +54 -0
  56. data/test/test_inject.rb +31 -0
  57. data/test/test_invalid.rb +28 -0
  58. data/test/test_ip.rb +69 -0
  59. data/test/test_ip6.rb +68 -0
  60. data/test/test_octets.rb +37 -0
  61. data/test/test_packet.rb +174 -0
  62. data/test/test_pcap.rb +209 -0
  63. data/test/test_structfu.rb +112 -0
  64. data/test/test_tcp.rb +327 -0
  65. data/test/test_udp.rb +73 -0
  66. data/test/vlan-pcapr.cap +0 -0
  67. metadata +85 -6
@@ -0,0 +1,594 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module StructFu
4
+
5
+ # Set the endianness for the various Int classes. Takes either :little or :big.
6
+ def set_endianness(e=nil)
7
+ unless [:little, :big].include? e
8
+ raise ArgumentError, "Unknown endianness for #{self.class}"
9
+ end
10
+ @int32 = e == :little ? Int32le : Int32be
11
+ @int16 = e == :little ? Int16le : Int16be
12
+ return e
13
+ end
14
+
15
+ # Instead of returning the "size" of the object, which is usually the
16
+ # number of elements of the Struct, returns the size of the object after
17
+ # a to_s. Essentially, a short version of self.to_size.size
18
+ def sz
19
+ self.to_s.size
20
+ end
21
+
22
+ end
23
+
24
+ module PacketFu
25
+
26
+ # PcapHeader represents the header portion of a libpcap file (the packets
27
+ # themselves are in the PcapPackets array). See
28
+ # http://wiki.wireshark.org/Development/LibpcapFileFormat for details.
29
+ #
30
+ # Depending on the endianness (set with :endian), elements are either
31
+ # :little endian or :big endian.
32
+ #
33
+ # ==== PcapHeader Definition
34
+ #
35
+ # Symbol :endian Default: :little
36
+ # Int32 :magic Default: 0xa1b2c3d4 # :big is 0xd4c3b2a1
37
+ # Int16 :ver_major Default: 2
38
+ # Int16 :ver_minor Default: 4
39
+ # Int32 :thiszone
40
+ # Int32 :sigfigs
41
+ # Int32 :snaplen Default: 0xffff
42
+ # Int32 :network Default: 1
43
+ class PcapHeader < Struct.new(:endian, :magic, :ver_major, :ver_minor,
44
+ :thiszone, :sigfigs, :snaplen, :network)
45
+ include StructFu
46
+
47
+ MAGIC_INT32 = 0xa1b2c3d4
48
+ MAGIC_LITTLE = [MAGIC_INT32].pack("V")
49
+ MAGIC_BIG = [MAGIC_INT32].pack("N")
50
+
51
+ def initialize(args={})
52
+ set_endianness(args[:endian] ||= :little)
53
+ init_fields(args)
54
+ super(args[:endian], args[:magic], args[:ver_major],
55
+ args[:ver_minor], args[:thiszone], args[:sigfigs],
56
+ args[:snaplen], args[:network])
57
+ end
58
+
59
+ # Called by initialize to set the initial fields.
60
+ def init_fields(args={})
61
+ args[:magic] = @int32.new(args[:magic] || PcapHeader::MAGIC_INT32)
62
+ args[:ver_major] = @int16.new(args[:ver_major] || 2)
63
+ args[:ver_minor] ||= @int16.new(args[:ver_minor] || 4)
64
+ args[:thiszone] ||= @int32.new(args[:thiszone])
65
+ args[:sigfigs] ||= @int32.new(args[:sigfigs])
66
+ args[:snaplen] ||= @int32.new(args[:snaplen] || 0xffff)
67
+ args[:network] ||= @int32.new(args[:network] || 1)
68
+ return args
69
+ end
70
+
71
+ # Returns the object in string form.
72
+ def to_s
73
+ self.to_a[1,7].map {|x| x.to_s}.join
74
+ end
75
+
76
+ # Reads a string to populate the object.
77
+ # TODO: Need to test this by getting a hold of a big endian pcap file.
78
+ # Conversion from big to little shouldn't be that big of a deal.
79
+ def read(str)
80
+ force_binary(str)
81
+ return self if str.nil?
82
+ str.force_encoding("binary") if str.respond_to? :force_encoding
83
+ if str[0,4] == self[:magic].to_s
84
+ self[:magic].read str[0,4]
85
+ self[:ver_major].read str[4,2]
86
+ self[:ver_minor].read str[6,2]
87
+ self[:thiszone].read str[8,4]
88
+ self[:sigfigs].read str[12,4]
89
+ self[:snaplen].read str[16,4]
90
+ self[:network].read str[20,4]
91
+ else
92
+ raise "Incorrect magic for libpcap"
93
+ end
94
+ self
95
+ end
96
+
97
+ end
98
+
99
+ # The Timestamp class defines how Timestamps appear in libpcap files.
100
+ #
101
+ # ==== Header Definition
102
+ #
103
+ # Symbol :endian Default: :little
104
+ # Int32 :sec
105
+ # Int32 :usec
106
+ class Timestamp < Struct.new(:endian, :sec, :usec)
107
+ include StructFu
108
+
109
+ def initialize(args={})
110
+ set_endianness(args[:endian] ||= :little)
111
+ init_fields(args)
112
+ super(args[:endian], args[:sec], args[:usec])
113
+ end
114
+
115
+ # Called by initialize to set the initial fields.
116
+ def init_fields(args={})
117
+ args[:sec] = @int32.new(args[:sec])
118
+ args[:usec] = @int32.new(args[:usec])
119
+ return args
120
+ end
121
+
122
+ # Returns the object in string form.
123
+ def to_s
124
+ self.to_a[1,2].map {|x| x.to_s}.join
125
+ end
126
+
127
+ # Reads a string to populate the object.
128
+ def read(str)
129
+ force_binary(str)
130
+ return self if str.nil?
131
+ self[:sec].read str[0,4]
132
+ self[:usec].read str[4,4]
133
+ self
134
+ end
135
+
136
+ end
137
+
138
+ # PcapPacket defines how individual packets are stored in a libpcap-formatted
139
+ # file.
140
+ #
141
+ # ==== Header Definition
142
+ #
143
+ # Timestamp :timestamp
144
+ # Int32 :incl_len
145
+ # Int32 :orig_len
146
+ # String :data
147
+ class PcapPacket < Struct.new(:endian, :timestamp, :incl_len,
148
+ :orig_len, :data)
149
+ include StructFu
150
+ def initialize(args={})
151
+ set_endianness(args[:endian] ||= :little)
152
+ init_fields(args)
153
+ super(args[:endian], args[:timestamp], args[:incl_len],
154
+ args[:orig_len], args[:data])
155
+ end
156
+
157
+ # Called by initialize to set the initial fields.
158
+ def init_fields(args={})
159
+ args[:timestamp] = Timestamp.new(:endian => args[:endian]).read(args[:timestamp])
160
+ args[:incl_len] = args[:incl_len].nil? ? @int32.new(args[:data].to_s.size) : @int32.new(args[:incl_len])
161
+ args[:orig_len] = @int32.new(args[:orig_len])
162
+ args[:data] = StructFu::String.new.read(args[:data])
163
+ end
164
+
165
+ # Returns the object in string form.
166
+ def to_s
167
+ self.to_a[1,4].map {|x| x.to_s}.join
168
+ end
169
+
170
+ # Reads a string to populate the object.
171
+ def read(str)
172
+ return unless str
173
+ force_binary(str)
174
+ self[:timestamp].read str[0,8]
175
+ self[:incl_len].read str[8,4]
176
+ self[:orig_len].read str[12,4]
177
+ self[:data].read str[16,self[:incl_len].to_i]
178
+ self
179
+ end
180
+
181
+ end
182
+
183
+ # PcapPackets is a collection of PcapPacket objects.
184
+ class PcapPackets < Array
185
+
186
+ include StructFu
187
+
188
+ attr_accessor :endian # probably ought to be read-only but who am i.
189
+
190
+ def initialize(args={})
191
+ @endian = args[:endian] || :little
192
+ end
193
+
194
+ def force_binary(str)
195
+ str.force_encoding "binary" if str.respond_to? :force_encoding
196
+ end
197
+
198
+ # Reads a string to populate the object. Note, this read takes in the
199
+ # whole pcap file, since we need to see the magic to know what
200
+ # endianness we're dealing with.
201
+ def read(str)
202
+ force_binary(str)
203
+ return self if str.nil?
204
+ if str[0,4] == PcapHeader::MAGIC_BIG
205
+ @endian = :big
206
+ elsif str[0,4] == PcapHeader::MAGIC_LITTLE
207
+ @endian = :little
208
+ else
209
+ raise ArgumentError, "Unknown file format for #{self.class}"
210
+ end
211
+ body = str[24,str.size]
212
+ while body.size > 16 # TODO: catch exceptions on malformed packets at end
213
+ p = PcapPacket.new(:endian => @endian)
214
+ p.read(body)
215
+ self<<p
216
+ body = body[p.sz,body.size]
217
+ end
218
+ self
219
+ end
220
+
221
+ def to_s
222
+ self.join
223
+ end
224
+
225
+ end
226
+
227
+ # PcapFile is a complete libpcap file struct, made up of two elements, a
228
+ # PcapHeader and PcapPackets.
229
+ #
230
+ # See http://wiki.wireshark.org/Development/LibpcapFileFormat
231
+ #
232
+ # PcapFile also can behave as a singleton class, which is usually the better
233
+ # way to handle pcap files of really any size, since it doesn't require
234
+ # storing packets before handing them off to a given block. This is really
235
+ # the way to go.
236
+ class PcapFile < Struct.new(:endian, :head, :body)
237
+ include StructFu
238
+
239
+ class << self
240
+
241
+ # Takes a given file and returns an array of the packet bytes. Here
242
+ # for backwards compatibilty.
243
+ def file_to_array(fname)
244
+ PcapFile.new.file_to_array(:f => fname)
245
+ end
246
+
247
+ # Takes a given file name, and reads out the packets. If given a block,
248
+ # it will yield back a PcapPacket object per packet found.
249
+ def read(fname,&block)
250
+ file_header = PcapHeader.new
251
+ pcap_packets = PcapPackets.new
252
+ unless File.readable? fname
253
+ raise ArgumentError, "Cannot read file `#{fname}'"
254
+ end
255
+ begin
256
+ file_handle = File.open(fname, "rb")
257
+ file_header.read file_handle.read(24)
258
+ packet_count = 0
259
+ pcap_packet = PcapPacket.new(:endian => file_header.endian)
260
+ while pcap_packet.read file_handle.read(16) do
261
+ len = pcap_packet.incl_len
262
+ pcap_packet.data = StructFu::String.new.read(file_handle.read(len.to_i))
263
+ packet_count += 1
264
+ if pcap_packet.data.size < len.to_i
265
+ warn "Packet ##{packet_count} is corrupted: expected #{len.to_i}, got #{pcap_packet.data.size}. Exiting."
266
+ break
267
+ end
268
+ pcap_packets << pcap_packet.clone
269
+ yield pcap_packets.last if block
270
+ end
271
+ ensure
272
+ file_handle.close
273
+ end
274
+ block ? packet_count : pcap_packets
275
+ end
276
+
277
+ # Takes a filename, and an optional block. If a block is given,
278
+ # yield back the raw packet data from the given file. Otherwise,
279
+ # return an array of parsed packets.
280
+ def read_packet_bytes(fname,&block)
281
+ count = 0
282
+ packets = [] unless block
283
+ read(fname) do |packet|
284
+ if block
285
+ count += 1
286
+ yield packet.data.to_s
287
+ else
288
+ packets << packet.data.to_s
289
+ end
290
+ end
291
+ block ? count : packets
292
+ end
293
+
294
+ alias :file_to_array :read_packet_bytes
295
+
296
+ # Takes a filename, and an optional block. If a block is given,
297
+ # yield back parsed packets from the given file. Otherwise, return
298
+ # an array of parsed packets.
299
+ #
300
+ # This is a brazillian times faster than the old methods of extracting
301
+ # packets from files.
302
+ def read_packets(fname,&block)
303
+ count = 0
304
+ packets = [] unless block
305
+ read_packet_bytes(fname) do |packet|
306
+ if block
307
+ count += 1
308
+ yield Packet.parse(packet)
309
+ else
310
+ packets << Packet.parse(packet)
311
+ end
312
+ end
313
+ block ? count : packets
314
+ end
315
+
316
+ end
317
+
318
+ def initialize(args={})
319
+ init_fields(args)
320
+ super(args[:endian], args[:head], args[:body])
321
+ end
322
+
323
+ # Called by initialize to set the initial fields.
324
+ def init_fields(args={})
325
+ args[:head] = PcapHeader.new(:endian => args[:endian]).read(args[:head])
326
+ args[:body] = PcapPackets.new(:endian => args[:endian]).read(args[:body])
327
+ return args
328
+ end
329
+
330
+ # Returns the object in string form.
331
+ def to_s
332
+ self[:head].to_s + self[:body].map {|p| p.to_s}.join
333
+ end
334
+
335
+ # Clears the contents of the PcapFile.
336
+ def clear
337
+ self[:body].clear
338
+ end
339
+
340
+ # Reads a string to populate the object. Note that this appends new packets to
341
+ # any existing packets in the PcapFile.
342
+ def read(str)
343
+ force_binary(str)
344
+ self[:head].read str[0,24]
345
+ self[:body].read str
346
+ self
347
+ end
348
+
349
+ # Clears the contents of the PcapFile prior to reading in a new string.
350
+ def read!(str)
351
+ clear
352
+ force_binary(str)
353
+ self.read str
354
+ end
355
+
356
+ # A shorthand method for opening a file and reading in the packets. Note
357
+ # that readfile clears any existing packets, since that seems to be the
358
+ # typical use.
359
+ def readfile(file)
360
+ fdata = File.open(file, "rb") {|f| f.read}
361
+ self.read! fdata
362
+ end
363
+
364
+ # file_to_array() translates a libpcap file into an array of packets.
365
+ # Note that this strips out pcap timestamps -- if you'd like to retain
366
+ # timestamps and other libpcap file information, you will want to
367
+ # use read() instead.
368
+ def file_to_array(args={})
369
+ filename = args[:filename] || args[:file] || args[:f]
370
+ if filename
371
+ self.read! File.open(filename, "rb") {|f| f.read}
372
+ end
373
+ if args[:keep_timestamps] || args[:keep_ts] || args[:ts]
374
+ self[:body].map {|x| {x.timestamp.to_s => x.data.to_s} }
375
+ else
376
+ self[:body].map {|x| x.data.to_s}
377
+ end
378
+ end
379
+
380
+ alias_method :f2a, :file_to_array
381
+
382
+ # Takes an array of packets (as generated by file_to_array), and writes them
383
+ # to a file. Valid arguments are:
384
+ #
385
+ # :filename
386
+ # :array # Can either be an array of packet data, or a hash-value pair of timestamp => data.
387
+ # :timestamp # Sets an initial timestamp
388
+ # :ts_inc # Sets the increment between timestamps. Defaults to 1 second.
389
+ # :append # If true, then the packets are appended to the end of a file.
390
+ def array_to_file(args={})
391
+ if args.kind_of? Hash
392
+ filename = args[:filename] || args[:file] || args[:f]
393
+ arr = args[:array] || args[:arr] || args[:a]
394
+ ts = args[:timestamp] || args[:ts] || Time.now.to_i
395
+ ts_inc = args[:timestamp_increment] || args[:ts_inc] || 1
396
+ append = !!args[:append]
397
+ elsif args.kind_of? Array
398
+ arr = args
399
+ filename = append = nil
400
+ else
401
+ raise ArgumentError, "Unknown argument. Need either a Hash or Array."
402
+ end
403
+ unless arr.kind_of? Array
404
+ raise ArgumentError, "Need an array to read packets from"
405
+ end
406
+ arr.each_with_index do |p,i|
407
+ if p.kind_of? Hash # Binary timestamps are included
408
+ this_ts = p.keys.first
409
+ this_incl_len = p.values.first.size
410
+ this_orig_len = this_incl_len
411
+ this_data = p.values.first
412
+ else # it's an array
413
+ this_ts = Timestamp.new(:endian => self[:endian], :sec => ts + (ts_inc * i)).to_s
414
+ this_incl_len = p.to_s.size
415
+ this_orig_len = this_incl_len
416
+ this_data = p.to_s
417
+ end
418
+ this_pkt = PcapPacket.new({:endian => self[:endian],
419
+ :timestamp => this_ts,
420
+ :incl_len => this_incl_len,
421
+ :orig_len => this_orig_len,
422
+ :data => this_data }
423
+ )
424
+ self[:body] << this_pkt
425
+ end
426
+ if filename
427
+ self.to_f(:filename => filename, :append => append)
428
+ else
429
+ self
430
+ end
431
+ end
432
+
433
+ alias_method :a2f, :array_to_file
434
+
435
+ # Just like array_to_file, but clears any existing packets from the array first.
436
+ def array_to_file!(arr)
437
+ clear
438
+ array_to_file(arr)
439
+ end
440
+
441
+ alias_method :a2f!, :array_to_file!
442
+
443
+ # Writes the PcapFile to a file. Takes the following arguments:
444
+ #
445
+ # :filename # The file to write to.
446
+ # :append # If set to true, the packets are appended to the file, rather than overwriting.
447
+ def to_file(args={})
448
+ filename = args[:filename] || args[:file] || args[:f]
449
+ unless (!filename.nil? || filename.kind_of?(String))
450
+ raise ArgumentError, "Need a :filename for #{self.class}"
451
+ end
452
+ append = args[:append]
453
+ if append
454
+ if File.exists? filename
455
+ File.open(filename,'ab') {|file| file.write(self.body.to_s)}
456
+ else
457
+ File.open(filename,'wb') {|file| file.write(self.to_s)}
458
+ end
459
+ else
460
+ File.open(filename,'wb') {|file| file.write(self.to_s)}
461
+ end
462
+ [filename, self.body.sz, self.body.size]
463
+ end
464
+
465
+ alias_method :to_f, :to_file
466
+
467
+ # Shorthand method for writing to a file. Can take either :file => 'name.pcap' or
468
+ # simply 'name.pcap'
469
+ def write(filename='out.pcap')
470
+ if filename.kind_of?(Hash)
471
+ f = filename[:filename] || filename[:file] || filename[:f] || 'out.pcap'
472
+ else
473
+ f = filename.to_s
474
+ end
475
+ self.to_file(:filename => f.to_s, :append => false)
476
+ end
477
+
478
+ # Shorthand method for appending to a file. Can take either :file => 'name.pcap' or
479
+ # simply 'name.pcap'
480
+ def append(filename='out.pcap')
481
+ if filename.kind_of?(Hash)
482
+ f = filename[:filename] || filename[:file] || filename[:f] || 'out.pcap'
483
+ else
484
+ f = filename.to_s
485
+ end
486
+ self.to_file(:filename => f, :append => true)
487
+ end
488
+
489
+ end
490
+
491
+ end
492
+
493
+ module PacketFu
494
+
495
+ # Read is largely deprecated. It was current in PacketFu 0.2.0, but isn't all that useful
496
+ # in 0.3.0 and beyond. Expect it to go away completely by version 1.0. So, the main use
497
+ # of this class is to learn how to do exactly the same things using the PcapFile object.
498
+ class Read
499
+
500
+ class << self
501
+
502
+ # Reads the magic string of a pcap file, and determines
503
+ # if it's :little or :big endian.
504
+ def get_byte_order(pcap_file)
505
+ byte_order = ((pcap_file[0,4] == PcapHeader::MAGIC_LITTLE) ? :little : :big)
506
+ return byte_order
507
+ end
508
+
509
+ # set_byte_order is pretty much totally deprecated.
510
+ def set_byte_order(byte_order)
511
+ PacketFu.instance_variable_set(:@byte_order,byte_order)
512
+ return true
513
+ end
514
+
515
+ # A wrapper for PcapFile#file_to_array, but only returns the array. Actually
516
+ # using the PcapFile object is going to be more useful.
517
+ def file_to_array(args={})
518
+ filename = args[:filename] || args[:file] || args[:out]
519
+ raise ArgumentError, "Need a :filename in string form to read from." if (filename.nil? || filename.class != String)
520
+ PcapFile.new.file_to_array(args)
521
+ end
522
+
523
+ alias_method :f2a, :file_to_array
524
+
525
+ end
526
+
527
+ end
528
+
529
+ end
530
+
531
+ module PacketFu
532
+
533
+ # Write is largely deprecated. It was current in PacketFu 0.2.0, but isn't all that useful
534
+ # in 0.3.0 and beyond. Expect it to go away completely by version 1.0, as working with
535
+ # PacketFu::PcapFile directly is generally going to be more rewarding.
536
+ class Write
537
+
538
+ class << self
539
+
540
+ # format_packets: Pretty much totally deprecated.
541
+ def format_packets(args={})
542
+ arr = args[:arr] || args[:array] || []
543
+ ts = args[:ts] || args[:timestamp] || Time.now.to_i
544
+ ts_inc = args[:ts_inc] || args[:timestamp_increment]
545
+ pkts = PcapFile.new.array_to_file(:endian => PacketFu.instance_variable_get(:@byte_order),
546
+ :arr => arr,
547
+ :ts => ts,
548
+ :ts_inc => ts_inc)
549
+ pkts.body
550
+ end
551
+
552
+ # array_to_file is a largely deprecated function for writing arrays of pcaps to a file.
553
+ # Use PcapFile#array_to_file instead.
554
+ def array_to_file(args={})
555
+ filename = args[:filename] || args[:file] || args[:out] || :nowrite
556
+ arr = args[:arr] || args[:array] || []
557
+ ts = args[:ts] || args[:timestamp] || args[:time_stamp] || Time.now.to_f
558
+ ts_inc = args[:ts_inc] || args[:timestamp_increment] || args[:time_stamp_increment]
559
+ byte_order = args[:byte_order] || args[:byteorder] || args[:endian] || args[:endianness] || :little
560
+ append = args[:append]
561
+ Read.set_byte_order(byte_order) if [:big, :little].include? byte_order
562
+ pf = PcapFile.new
563
+ pf.array_to_file(:endian => PacketFu.instance_variable_get(:@byte_order),
564
+ :arr => arr,
565
+ :ts => ts,
566
+ :ts_inc => ts_inc)
567
+ if filename && filename != :nowrite
568
+ if append
569
+ pf.append(filename)
570
+ else
571
+ pf.write(filename)
572
+ end
573
+ return [filename,pf.to_s.size,arr.size,ts,ts_inc]
574
+ else
575
+ return [nil,pf.to_s.size,arr.size,ts,ts_inc]
576
+ end
577
+
578
+ end
579
+
580
+ alias_method :a2f, :array_to_file
581
+
582
+ # Shorthand method for appending to a file. Also shouldn't use.
583
+ def append(args={})
584
+ array_to_file(args.merge(:append => true))
585
+ end
586
+
587
+ end
588
+
589
+ end
590
+
591
+ end
592
+
593
+
594
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby