spcap 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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