spcap 0.0.2 → 0.0.3

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/.gitignore CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ pcapcopy.tmp
19
+
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Spcap
2
2
 
3
- TODO: Write a gem description
3
+ Simple Pcap stream handler without native extention.
4
+ This gem respect original interface of ruby-pcap module
5
+
6
+ [![Gem Version](https://badge.fury.io/rb/spcap.png)](http://badge.fury.io/rb/spcap)
4
7
 
5
8
  ## Installation
6
9
 
@@ -1,8 +1,14 @@
1
1
  require 'log4r'
2
2
  require 'spcap/version'
3
+ require 'spcap/exceptions'
3
4
  require 'spcap/logger'
5
+ require 'spcap/ipaddress'
4
6
  require 'spcap/packet'
5
- require 'spcap/file'
7
+ require 'spcap/ippacket'
8
+ require 'spcap/tcppacket'
9
+ require 'spcap/factory'
10
+ require 'spcap/stream'
11
+ require 'spcap/scopy'
6
12
 
7
13
  module Spcap
8
14
  # Your code goes here...
@@ -0,0 +1,4 @@
1
+ module Spcap
2
+ class InitializeException < Exception
3
+ end
4
+ end
@@ -13,7 +13,10 @@ module Spcap
13
13
  return TCPPacket.new(time,raw_data,len,linklayer_header_type)
14
14
  end
15
15
  end
16
- Logger.warn "Spcap::Factory only support TCP over IPv4 packet other packet are dropped"
16
+
17
+ p = Packet.new(time,raw_data,len,linklayer_header_type)
18
+ Logger.warn "Spcap::Factory only support TCP over IPv4 packet other packet are dropped Packet Headher : [#{raw_data[0,16].unpack("H*").first}]"
19
+ return p
17
20
  end
18
21
  end
19
22
  end
@@ -6,11 +6,18 @@ module Spcap
6
6
  @address = address
7
7
  end
8
8
 
9
+ # Return true if two addresses are the same address.
10
+ def <=>(other)
11
+ @address <=> other.address
12
+ end
13
+
9
14
  # Return true if two addresses are the same address.
10
15
  def ==(other)
11
- @address = other.adress
16
+ @address == other.address
12
17
  end
13
18
 
19
+ def eql?(other) ; self == other ; end
20
+
14
21
  def hash
15
22
  @address.hash
16
23
  end
@@ -21,7 +28,7 @@ module Spcap
21
28
  to_num_s
22
29
  end
23
30
 
24
- Return the value of IP address as integer.
31
+ # Return the value of IP address as integer.
25
32
  def to_i
26
33
  @address.unpackt("N").first
27
34
  end
@@ -1,24 +1,19 @@
1
1
  module Spcap
2
2
  class IPPacket < Packet
3
- attr_reader :src,:dst,:iph_len,:ip_len,
3
+ attr_reader :src,:dst,:ip_hlen,:ip_len
4
+
4
5
  def initialize(time,data,len,datalink)
5
6
  super(time,data,len,datalink)
6
7
  @src = IPAddress.new(@raw_data[12,4])
7
8
  @dst = IPAddress.new(data[16,4])
8
- @iph_len = @raw_data.getbyte(0) & 0x0F
9
+ @ip_hlen = @raw_data.getbyte(0) & 0x0F
9
10
  @ip_len = @raw_data[2,2].unpack("n").first
10
11
 
11
12
  end
12
13
 
13
-
14
- # Return header length. (Unit: 4 octets)
15
- def ip_hlen
16
- @raw_data.getbyte(0) & 0x0F
17
- end
18
-
19
14
  # Return data part as String.
20
15
  def ip_data
21
- @raw_data[ip_hlen,self.caplen-ip_hlen]
16
+ @raw_data[ip_hlen*4,self.caplen-ip_hlen*4]
22
17
  end
23
18
 
24
19
  #
@@ -15,7 +15,6 @@ module Spcap
15
15
  def ip? ; self.kind_of?(IPPacket) ; end
16
16
  def tcp? ; self.kind_of?(TCPPacket) ; end
17
17
  def udp? ; self.kind_of?(UDPPacket) ; end
18
- def time_i ; self.time.to_i; end
19
-
18
+ def time_i ; self.time.to_i ; end
20
19
  end
21
20
  end
@@ -0,0 +1,50 @@
1
+ module Spcap
2
+ class Scopy < Stream
3
+ CURRENT_OUT_FILENAME = 'pcapcopy.tmp'
4
+ FILENAME_EXTENTION = '.pcap'
5
+ def initialize(istream,opt = {})
6
+ @tmpname = CURRENT_OUT_FILENAME
7
+ @wpath = opt[:wpath] || ''
8
+ @prefix = opt[:prefix] || ''
9
+ @limit = opt[:limit] || 10000
10
+ @counter = 0
11
+ @ostream = new_file
12
+ super(istream)
13
+ packing = @unpack_16 + @unpack_16 + @unpack_32 + @unpack_32 + @unpack_32 + @unpack_32
14
+ @header = @magic_number + [@major_version, @minor_version, 0, 0, @snapshot_length,
15
+ @linklayer_header_type].pack(packing)
16
+ end
17
+
18
+ def read(size)
19
+ buf = @istream.read(size)
20
+ @ostream.write(buf)
21
+ return buf
22
+ end
23
+
24
+ def new_file ; File.new(File.expand_path(backup_name,@wpath),"w") ; end
25
+
26
+ def backup_name
27
+ @prefix + Time.now.strftime("%Y%m%d%H%M%S%6N") + FILENAME_EXTENTION
28
+ end
29
+
30
+ def switch_out
31
+ @ostream.close
32
+ @ostream = new_file
33
+ @ostream.write(@header)
34
+ end
35
+
36
+ def finalize
37
+ @ostream.close
38
+ end
39
+
40
+ def next
41
+ pkt = super
42
+ @counter += 1
43
+ if @counter == @limit
44
+ switch_out
45
+ @counter = 0
46
+ end
47
+ return pkt
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,87 @@
1
+ module Spcap
2
+ class Stream
3
+ # File format :
4
+ # File header
5
+ # 4 Magic number
6
+ # 2,2 Major version Minor version
7
+ # 4 Time zone offset always set to 0
8
+ # 4 Time stamp accuracy always set to 0
9
+ # 4 Snapshot length
10
+ # 4 Link-layer header type
11
+
12
+ MagicNumber = ["A1B2C3D4"].pack("H*")
13
+
14
+ def initialize(istream)
15
+ @istream = istream
16
+ @magic_number = read(4)
17
+ if @magic_number == MagicNumber
18
+ @unpack_16 = "n"
19
+ @unpack_32 = "N"
20
+ else
21
+ @unpack_16 = "v"
22
+ @unpack_32 = "V"
23
+ end
24
+ @major_version, @minor_version = read16, read16
25
+ read(8) # flush unused time_zone_offset_always_0, timestamp_accuracy_always_0,
26
+ @snapshot_length = read32
27
+ @linklayer_header_type = read32
28
+ # if header type is not ethernet raise an error !!
29
+ raise InitializeException, "Not PCAP ethernet stream is not supported"if @linklayer_header_type != 1
30
+
31
+ end
32
+ def close
33
+ @istream.close
34
+ end
35
+ def read(size)
36
+ buf = @istream.read(size)
37
+ return buf
38
+ end
39
+
40
+ def read16
41
+ buf = read(2)
42
+ buf.unpack(@unpack_16).first
43
+ end
44
+
45
+ def read32
46
+ buf = read(4)
47
+ buf.unpack(@unpack_32).first
48
+ end
49
+ # Packets header
50
+ # 4 Time stamp, seconds value
51
+ # 4 Time stamp, microseconds value
52
+ # 4 Length of captured packet data
53
+ # 4 Un-truncated length of the packet data
54
+ def each
55
+ until(@istream.eof?)
56
+ p = self.next
57
+ yield p unless p.nil?
58
+ end
59
+ end
60
+
61
+ def eof? ; @istream.eof? ; end
62
+
63
+ def next
64
+ time = Time.at(read32,read32)
65
+ caplen = read32
66
+ len = read32
67
+ # TODO : move Ethernet parsing in Packet class constructor
68
+ src_mac_address = read(6)
69
+ dst_mac_address = read(6)
70
+ protocol_type = read(2).unpack("n").first
71
+ raw_data = read(caplen-14)
72
+ if protocol_type == 0x0800
73
+ p = Factory.get_packet(time,raw_data,len,@linklayer_header_type)
74
+ if p.nil?
75
+ Logger.warn "Spcap::Factory return nil packet"
76
+ else
77
+ return p
78
+ end
79
+ else
80
+ # ignore non IPv4 packets
81
+ Logger.info "Non-IPv4 packets are ignored Protocol = #{protocol_type}"
82
+ end
83
+ return nil
84
+ end
85
+
86
+ end
87
+ end
@@ -5,11 +5,16 @@ module Spcap
5
5
  super(time,data,len,datalink)
6
6
  end
7
7
 
8
+ def channel ; [[src,sport],[dst,dport]] ; end
9
+ def full_session ; channel.sort ; end
10
+ def session ; full_session.hash ; end
11
+ def direction ; full_session == channel ; end
12
+
8
13
  # Return acknowledgement number.
9
14
  def tcp_ack ; ip_data[8,4].unpack("N").first ; end
10
15
 
11
16
  # Return data part as String.
12
- def tcp_data ; ip_data[tcp_off,tcp_data_len] ; end
17
+ def tcp_data ; ip_data[tcp_off*4,tcp_data_len] ; end
13
18
 
14
19
  # Return length of data part.
15
20
  def tcp_data_len ; ip_len - ( ip_hlen * 4 ) - (tcp_hlen * 4) ; end
@@ -34,12 +39,12 @@ module Spcap
34
39
  end
35
40
 
36
41
  # Return true if flag is set.
37
- def tcp_fin? ; flags?(7) ; end
38
- def tcp_syn? ; flags?(6) ; end
39
- def tcp_rst? ; flags?(5) ; end
40
- def tcp_psh? ; flags?(4) ; end
41
- def tcp_ack? ; flags?(3) ; end
42
- def tcp_urg? ; flags?(2) ; end
42
+ def tcp_fin? ; flag?(7) ; end
43
+ def tcp_syn? ; flag?(6) ; end
44
+ def tcp_rst? ; flag?(5) ; end
45
+ def tcp_psh? ; flag?(4) ; end
46
+ def tcp_ack? ; flag?(3) ; end
47
+ def tcp_urg? ; flag?(2) ; end
43
48
 
44
49
  # Return TCP data offset (header length). (Unit: 4-octets)
45
50
  def tcp_hlen ; ( ( ip_data.getbyte(12) & 0XF0) / 16 ) ; end
@@ -1,3 +1,3 @@
1
1
  module Spcap
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,10 +1,10 @@
1
1
  require_relative '../../test_helper'
2
2
 
3
- describe Spcap::File do
4
- subject { Spcap::File.new(File.open('test/pcap_files/sample.pcap')) }
3
+ describe Spcap::Stream do
4
+ subject { Spcap::Stream.new(File.open('test/pcap_files/sample.pcap')) }
5
5
 
6
- it "must be a Spcap::File" do
7
- subject.must_be_instance_of(Spcap::File)
6
+ it "must be a Spcap::Stream" do
7
+ subject.must_be_instance_of(Spcap::Stream)
8
8
  end
9
9
 
10
10
  it "must handle each without error" do
@@ -13,3 +13,44 @@ describe Spcap::File do
13
13
  end
14
14
 
15
15
  end
16
+
17
+ describe Spcap::Scopy do
18
+ subject { Spcap::Scopy.new(File.open('test/pcap_files/sample.pcap')) }
19
+ before do
20
+ @counter=0
21
+ end
22
+ it "must be a Spcap::Scopy" do
23
+ subject.must_be_instance_of(Spcap::Scopy)
24
+ end
25
+
26
+ it "must handle each without error" do
27
+ subject.each{|p| @counter += 1}
28
+ @counter.must_equal(37)
29
+ subject.must_respond_to(:each)
30
+ end
31
+ end
32
+
33
+
34
+ describe Spcap::TCPPacket do
35
+
36
+ subject { packet = nil
37
+ src = Spcap::Scopy.new(File.open('test/pcap_files/sample.pcap'))
38
+ packet = src.next
39
+ packet
40
+ }
41
+ before do
42
+ @src = Spcap::IPAddress.new([174,100,92,122].pack("CCCC"))
43
+ @sport = 45671
44
+ @dst = Spcap::IPAddress.new([174,100,92,106].pack("CCCC"))
45
+ @dport = 111
46
+ @channel = [[@src,@sport],[@dst,@dport]]
47
+ @full_session = @channel.sort
48
+ end
49
+ it "must be a Spcap::TCPPacket" do
50
+ subject.must_be_instance_of(Spcap::TCPPacket)
51
+ end
52
+
53
+ it "must handle full_session without error" do
54
+ subject.full_session.must_equal(@full_session)
55
+ end
56
+ end
@@ -2,3 +2,4 @@ require 'minitest/spec'
2
2
  require 'minitest/autorun'
3
3
  require 'minitest/pride'
4
4
  require File.expand_path('../../lib/spcap.rb', __FILE__)
5
+ Spcap::Logger.level=Log4r::DEBUG
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spcap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-23 00:00:00.000000000 Z
12
+ date: 2013-10-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -56,16 +56,19 @@ files:
56
56
  - README.md
57
57
  - Rakefile
58
58
  - lib/spcap.rb
59
+ - lib/spcap/exceptions.rb
59
60
  - lib/spcap/factory.rb
60
- - lib/spcap/file.rb
61
- - lib/spcap/ipadress.rb
61
+ - lib/spcap/ipaddress.rb
62
62
  - lib/spcap/ippacket.rb
63
63
  - lib/spcap/logger.rb
64
64
  - lib/spcap/packet.rb
65
+ - lib/spcap/scopy.rb
66
+ - lib/spcap/stream.rb
65
67
  - lib/spcap/tcppacket.rb
66
68
  - lib/spcap/version.rb
67
69
  - spcap.gemspec
68
70
  - test/lib/spcap/file_test.rb
71
+ - test/pcap_files/sample.pcap
69
72
  - test/test_helper.rb
70
73
  homepage: https://github.com/brodier/spcap
71
74
  licenses:
@@ -95,4 +98,5 @@ summary: Pure ruby gem without native exstension that handle pcap file produce b
95
98
  pcap library or tcpdump
96
99
  test_files:
97
100
  - test/lib/spcap/file_test.rb
101
+ - test/pcap_files/sample.pcap
98
102
  - test/test_helper.rb
@@ -1,48 +0,0 @@
1
- module Spcap
2
- class File
3
- # File format :
4
- # File header
5
- # 4 Magic number
6
- # 2,2 Major version Minor version
7
- # 4 Time zone offset always set to 0
8
- # 4 Time stamp accuracy always set to 0
9
- # 4 Snapshot length
10
- # 4 Link-layer header type
11
-
12
- MagicNumber = ["A1B2C3D4"].pack("H*")
13
-
14
- def initialize(istream)
15
- @istream = istream
16
- magic_number = istream.read(4)
17
- if magic_number == MagicNumber
18
- @unpack_16 = "n"
19
- @unpack_32 = "N"
20
- else
21
- @unpack_16 = "v"
22
- @unpack_32 = "V"
23
- end
24
- @major_version, @minor_version = read16, read16
25
- @istream.read(8) # flush unused time_zone_offset_always_0, timestamp_accuracy_always_0,
26
- @snapshot_length = read32
27
- @linklayer_header_type = read32
28
- end
29
-
30
- def read16 ; @istream.read(2).unpack(@unpack_16).first ; end
31
-
32
- def read32 ; @istream.read(4).unpack(@unpack_32).first ; end
33
- # Packets header
34
- # 4 Time stamp, seconds value
35
- # 4 Time stamp, microseconds value
36
- # 4 Length of captured packet data
37
- # 4 Un-truncated length of the packet data
38
- def each
39
- until(@istream.eof?)
40
- time = Time.at(read32,read32)
41
- caplen = read32
42
- len = read32
43
- raw_data = @istream.read(caplen)
44
- yield Packet.new(time,raw_data,len,@linklayer_header_type)
45
- end
46
- end
47
- end
48
- end