packetgen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,145 @@
1
+ module PacketGen
2
+ module PcapNG
3
+
4
+ # {IDB} represents a Interface Description Block (IDB) of a pcapng file.
5
+ #
6
+ # == IDB Definition
7
+ # Int32 :type Default: 0x00000001
8
+ # Int32 :block_len
9
+ # Int16 :link_type Default: 1
10
+ # Int16 :reserved Default: 0
11
+ # Int64 :snaplen Default: 0 (no limit)
12
+ # String :options
13
+ # Int32 :block_len2
14
+ class IDB < Struct.new(:type, :block_len, :link_type, :reserved,
15
+ :snaplen, :options, :block_len2)
16
+ include StructFu
17
+ include Block
18
+
19
+ # @return [:little, :big]
20
+ attr_accessor :endian
21
+ # @return [SHB]
22
+ attr_accessor :section
23
+ # @return [Array<EPB,SPB>]
24
+ attr_accessor :packets
25
+
26
+ # Minimum IDB size
27
+ MIN_SIZE = 5*4
28
+
29
+ # Option code for if_tsresol option
30
+ OPTION_IF_TSRESOL = 9
31
+
32
+ # @param [Hash] options
33
+ # @option options [:little, :big] :endian set block endianness
34
+ # @option options [Integer] :type
35
+ # @option options [Integer] :block_len block total length
36
+ # @option options [Integer] :link_type
37
+ # @option options [Integer] :reserved
38
+ # @option options [Integer] :snaplen maximum number of octets captured from
39
+ # each packet
40
+ # @option options [::String] :options
41
+ # @option options [Integer] :block_len2 block total length
42
+ def initialize(options={})
43
+ @endian = set_endianness(options[:endian] || :little)
44
+ @packets = []
45
+ @options_decoded = false
46
+ init_fields(options)
47
+ super(options[:type], options[:block_len], options[:link_type], options[:reserved],
48
+ options[:snaplen], options[:options], options[:block_len2])
49
+ end
50
+
51
+ # Used by {#initialize} to set the initial fields
52
+ # @see #initialize possible options
53
+ # @param [Hash] options
54
+ # @return [Hash] return +options+
55
+ def init_fields(options={})
56
+ options[:type] = @int32.new(options[:type] || PcapNG::IDB_TYPE.to_i)
57
+ options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
58
+ options[:link_type] = @int16.new(options[:link_type] || 1)
59
+ options[:reserved] = @int16.new(options[:reserved] || 0)
60
+ options[:snaplen] = @int32.new(options[:snaplen] || 0)
61
+ options[:options] = StructFu::String.new(options[:options] || '')
62
+ options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
63
+ options
64
+ end
65
+
66
+ # Reads a String or a IO to populate the object
67
+ # @param [::String,IO] str_or_io
68
+ # @return [self]
69
+ def read(str_or_io)
70
+ if str_or_io.respond_to? :read
71
+ io = str_or_io
72
+ else
73
+ io = StringIO.new(force_binary(str_or_io.to_s))
74
+ end
75
+ return self if io.eof?
76
+
77
+ self[:type].read io.read(4)
78
+ self[:block_len].read io.read(4)
79
+ self[:link_type].read io.read(2)
80
+ self[:reserved].read io.read(2)
81
+ self[:snaplen].read io.read(4)
82
+ self[:options].read io.read(self[:block_len].to_i - MIN_SIZE)
83
+ self[:block_len2].read io.read(4)
84
+
85
+ unless self[:block_len].to_i == self[:block_len2].to_i
86
+ raise InvalidFileError, 'Incoherency in Interface Description Block'
87
+ end
88
+
89
+ self
90
+ end
91
+
92
+ # Add a xPB to this section
93
+ # @param [EPB,SPB] xpb
94
+ # @return [self]
95
+ def <<(xpb)
96
+ @packets << xpb
97
+ self
98
+ end
99
+
100
+ # Give timestamp resolution for this interface
101
+ # @param [Boolean] force if +true+, force decoding even if already done
102
+ # @return [Float]
103
+ def ts_resol(force: false)
104
+ if @options_decoded and not force
105
+ @ts_resol
106
+ else
107
+ packstr = (@endian == :little) ? 'v' : 'n'
108
+ idx = 0
109
+ options = self[:options]
110
+ opt_code = opt_len = 0
111
+
112
+ while idx < options.length do
113
+ opt_code, opt_len = options[idx, 4].unpack("#{packstr}2")
114
+ if opt_code == OPTION_IF_TSRESOL and opt_len == 1
115
+ tsresol = options[idx+4, 1].unpack('C').first
116
+ if tsresol & 0x80 == 0
117
+ @ts_resol = 10 ** -tsresol
118
+ else
119
+ @ts_resol = 2 ** -(tsresol & 0x7f)
120
+ end
121
+
122
+ @options_decoded = true
123
+ return @ts_resol
124
+ else
125
+ idx += 4 + opt_len
126
+ end
127
+ end
128
+
129
+ @options_decoded = true
130
+ @ts_resol = 1E-6 # default value
131
+ end
132
+ end
133
+
134
+ # Return the object as a String
135
+ # @return [String]
136
+ def to_s
137
+ pad_field :options
138
+ recalc_block_len
139
+ to_a.map(&:to_s).join + @packets.map(&:to_s).join
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+ end
@@ -0,0 +1,173 @@
1
+ module PacketGen
2
+ module PcapNG
3
+
4
+ # {SHB} represents a Section Header Block (SHB) of a pcapng file.
5
+ #
6
+ # == SHB Definition
7
+ # Int32 :type Default: 0x0A0D0D0A
8
+ # Int32 :block_len
9
+ # Int32 :magic Default: 0x1A2B3C4D # :big is 0x4D3C2C1A
10
+ # Int16 :ver_major Default: 1
11
+ # Int16 :ver_minor Default: 0
12
+ # Int64 :section_len
13
+ # String :options Default: ''
14
+ # Int32 :block_len2
15
+ class SHB < Struct.new(:type, :block_len, :magic, :ver_major, :ver_minor,
16
+ :section_len, :options, :block_len2)
17
+ include StructFu
18
+ include Block
19
+
20
+ # @return [:little, :big]
21
+ attr_accessor :endian
22
+ # Get interfaces for this section
23
+ # @return [Array<IDB>]
24
+ attr_reader :interfaces
25
+ # Get unsupported blocks given in pcapng file as raw data
26
+ # @return [Array<UnknownBlock>]
27
+ attr_reader :unknown_blocks
28
+
29
+ # Magic value to retrieve SHB
30
+ MAGIC_INT32 = 0x1A2B3C4D
31
+ # Magic value (little endian version)
32
+ MAGIC_LITTLE = [MAGIC_INT32].pack('V')
33
+ # Magic value (big endian version)
34
+ MAGIC_BIG = [MAGIC_INT32].pack('N')
35
+
36
+ # Minimum SHB size
37
+ MIN_SIZE = 7*4
38
+ # +section_len+ value for undefined length
39
+ SECTION_LEN_UNDEFINED = 0xffffffff_ffffffff
40
+
41
+ # @param [Hash] options
42
+ # @option options [:little, :big] :endian set block endianness
43
+ # @option options [Integer] :type
44
+ # @option options [Integer] :block_len block total length
45
+ # @option options [Integer] :magic magic number to distinguish little endian
46
+ # sessions and big endian ones
47
+ # @option options [Integer] :ver_major number of the current major version of
48
+ # the format
49
+ # @option options [Integer] :ver_minor number of the current minor version of
50
+ # the format
51
+ # @option options [Integer] :section_len length of following section, excluding
52
+ # he SHB itself
53
+ # @option options [::String] :options
54
+ # @option options [Integer] :block_len2 block total length
55
+ def initialize(options={})
56
+ @endian = set_endianness(options[:endian] || :little)
57
+ @interfaces = []
58
+ @unknown_blocks = []
59
+ init_fields(options)
60
+ super(options[:type], options[:block_len], options[:magic], options[:ver_major],
61
+ options[:ver_minor], options[:section_len], options[:options], options[:block_len2])
62
+ end
63
+
64
+ # Used by {#initialize} to set the initial fields
65
+ # @see #initialize possible options
66
+ # @param [Hash] options
67
+ # @return [Hash] return +options+
68
+ def init_fields(options={})
69
+ options[:type] = @int32.new(options[:type] || PcapNG::SHB_TYPE.to_i)
70
+ options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
71
+ options[:magic] = @int32.new(options[:magic] || MAGIC_INT32)
72
+ options[:ver_major] = @int16.new(options[:ver_major] || 1)
73
+ options[:ver_minor] = @int16.new(options[:ver_minor] || 0)
74
+ options[:section_len] = @int64.new(options[:section_len] || SECTION_LEN_UNDEFINED)
75
+ options[:options] = StructFu::String.new(options[:options] || '')
76
+ options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
77
+ options
78
+ end
79
+
80
+ # Reads a String or a IO to populate the object
81
+ # @param [::String,IO] str_or_io
82
+ # @return [self]
83
+ def read(str_or_io)
84
+ if str_or_io.respond_to? :read
85
+ io = str_or_io
86
+ else
87
+ io = StringIO.new(force_binary(str_or_io.to_s))
88
+ end
89
+ return self if io.eof?
90
+
91
+ type_str = io.read(4)
92
+ unless type_str == PcapNG::SHB_TYPE.to_s
93
+ type = type_str.unpack('H*').join
94
+ raise InvalidFileError, "Incorrect type (#{type})for Section Header Block"
95
+ end
96
+
97
+ block_len_str = io.read(4)
98
+
99
+ magic_str = io.read(4)
100
+ case @endian
101
+ when :little
102
+ case magic_str
103
+ when MAGIC_LITTLE
104
+ when MAGIC_BIG
105
+ force_endianness :big
106
+ else
107
+ raise InvalidFileError, 'Incorrect magic for Section Header Block'
108
+ end
109
+ when :big
110
+ case magic_str
111
+ when MAGIC_BIG
112
+ when MAGIC_LITTLE
113
+ force_endianness :little
114
+ else
115
+ raise InvalidFileError, 'Incorrect magic for Section Header Block'
116
+ end
117
+ end
118
+
119
+ self[:type].read type_str
120
+ self[:block_len].read block_len_str
121
+ self[:magic].read magic_str
122
+ self[:ver_major].read io.read(2)
123
+ self[:ver_minor].read io.read(2)
124
+ self[:section_len].read io.read(8)
125
+ self[:options].read io.read(self[:block_len].to_i - MIN_SIZE)
126
+ self[:block_len2].read io.read(4)
127
+
128
+ unless self[:block_len].to_i == self[:block_len2].to_i
129
+ raise InvalidFileError, 'Incoherency in Section Header Block'
130
+ end
131
+
132
+ self
133
+ end
134
+
135
+ # Add a IDB to this section
136
+ # @param [IDB] idb
137
+ # @return [self]
138
+ def <<(idb)
139
+ @interfaces << idb
140
+ self
141
+ end
142
+
143
+ # Return the object as a String
144
+ # @return [String]
145
+ def to_s
146
+ body = @interfaces.map(&:to_s).join
147
+ unless self[:section_len].to_i == SECTION_LEN_UNDEFINED
148
+ self.section_len.value = body.size
149
+ end
150
+ pad_field :options
151
+ recalc_block_len
152
+ to_a.map(&:to_s).join + body
153
+ end
154
+
155
+
156
+ private
157
+
158
+ def force_endianness(endian)
159
+ set_endianness endian
160
+ @endian = endian
161
+ self[:type] = @int32.new(self[:type].to_i)
162
+ self[:block_len] = @int32.new(self[:block_len].to_i)
163
+ self[:magic] = @int32.new(self[:magic].to_i)
164
+ self[:ver_major] = @int16.new(self[:ver_major].to_i)
165
+ self[:ver_minor] = @int16.new(self[:ver_minor].to_i)
166
+ self[:section_len] = @int64.new(self[:section_len].to_i)
167
+ self[:block_len2] = @int32.new(self[:block_len2].to_i)
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,103 @@
1
+ module PacketGen
2
+ module PcapNG
3
+
4
+ # {SPB} represents a Section Simple Packet Block (SPB) of a pcapng file.
5
+ #
6
+ # == Pcapng::SPB Definition
7
+ # Int32 :type Default: 0x00000003
8
+ # Int32 :block_len
9
+ # Int32 :orig_len
10
+ # String :data
11
+ # Int32 :block_len2
12
+ class SPB < Struct.new(:type, :block_len, :orig_len, :data, :block_len2)
13
+ include StructFu
14
+ include Block
15
+
16
+ # @return [:little, :big]
17
+ attr_accessor :endian
18
+ # @return [IPB]
19
+ attr_accessor :interface
20
+
21
+ # Minimum SPB size
22
+ MIN_SIZE = 4*4
23
+
24
+ # @param [Hash] options
25
+ # @option options [:little, :big] :endian set block endianness
26
+ # @option options [Integer] :type
27
+ # @option options [Integer] :block_len block total length
28
+ # @option options [Integer] :orig_len actual length of the packet when it was
29
+ # transmitted on the network
30
+ # @option options [::String] :data
31
+ # @option options [::String] :options
32
+ # @option options [Integer] :block_len2 block total length
33
+ def initialize(options={})
34
+ @endian = set_endianness(options[:endian] || :little)
35
+ init_fields(options)
36
+ super(options[:type], options[:block_len], options[:orig_len], options[:data],
37
+ options[:block_len2])
38
+ end
39
+
40
+ # Used by {#initialize} to set the initial fields
41
+ # @param [Hash] options
42
+ # @see #initialize possible options
43
+ # @return [Hash] return +options+
44
+ def init_fields(options={})
45
+ options[:type] = @int32.new(options[:type] || PcapNG::SPB_TYPE.to_i)
46
+ options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
47
+ options[:orig_len] = @int32.new(options[:orig_len] || 0)
48
+ options[:data] = StructFu::String.new(options[:data] || '')
49
+ options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
50
+ options
51
+ end
52
+
53
+ # Has this block option?
54
+ # @return [false]
55
+ def has_options?
56
+ false
57
+ end
58
+
59
+ # Reads a String or a IO to populate the object
60
+ # @param [::String,IO] str_or_io
61
+ # @return [self]
62
+ def read(str_or_io)
63
+ if str_or_io.respond_to? :read
64
+ io = str_or_io
65
+ else
66
+ io = StringIO.new(force_binary(str_or_io.to_s))
67
+ end
68
+ return self if io.eof?
69
+
70
+ self[:type].read io.read(4)
71
+ self[:block_len].read io.read(4)
72
+ self[:orig_len].read io.read(4)
73
+ # Take care of IDB snaplen
74
+ # CAUTION: snaplen == 0 -> no capture limit
75
+ if interface and interface.snaplen.to_i > 0
76
+ data_len = [self[:orig_len].to_i, interface.snaplen.to_i].min
77
+ else
78
+ data_len = self[:orig_len].to_i
79
+ end
80
+ data_pad_len = (4 - (data_len % 4)) % 4
81
+ self[:data].read io.read(data_len)
82
+ io.read data_pad_len
83
+ self[:block_len2].read io.read(4)
84
+
85
+ unless self[:block_len].to_i == self[:block_len2].to_i
86
+ raise InvalidFileError, 'Incoherency in Simple Packet Block'
87
+ end
88
+
89
+ self
90
+ end
91
+
92
+ # Return the object as a String
93
+ # @return [String]
94
+ def to_s
95
+ pad_field :data
96
+ recalc_block_len
97
+ to_a.map(&:to_s).join
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,80 @@
1
+ module PacketGen
2
+ module PcapNG
3
+
4
+ # {UnknownBlock} is used to handle unsupported blocks of a pcapng file.
5
+ class UnknownBlock < Struct.new(:type, :block_len, :body, :block_len2)
6
+ include StructFu
7
+ include Block
8
+
9
+ # @return [:little, :big]
10
+ attr_accessor :endian
11
+ # @return [SHB]
12
+ attr_accessor :section
13
+
14
+ # Minimum Iblock size
15
+ MIN_SIZE = 12
16
+
17
+ # @option options [:little, :big] :endian set block endianness
18
+ # @option options [Integer] :type
19
+ # @option options [Integer] :block_len block total length
20
+ # @option options [::String] :body
21
+ # @option options [Integer] :block_len2 block total length
22
+ def initialize(options={})
23
+ @endian = set_endianness(options[:endian] || :little)
24
+ init_fields(options)
25
+ super(options[:type], options[:block_len], options[:body], options[:block_len2])
26
+ end
27
+
28
+ # Used by {#initialize} to set the initial fields
29
+ # @see #initialize possible options
30
+ # @param [Hash] options
31
+ # @return [Hash] return +options+
32
+ def init_fields(options={})
33
+ options[:type] = @int32.new(options[:type] || 0)
34
+ options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
35
+ options[:body] = StructFu::String.new(options[:body] || '')
36
+ options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
37
+ options
38
+ end
39
+
40
+ # Has this block option?
41
+ # @return [false]
42
+ def has_options?
43
+ false
44
+ end
45
+
46
+ # Reads a String or a IO to populate the object
47
+ # @param [::String,IO] str_or_io
48
+ # @return [self]
49
+ def read(str_or_io)
50
+ if str_or_io.respond_to? :read
51
+ io = str_or_io
52
+ else
53
+ io = StringIO.new(force_binary(str_or_io.to_s))
54
+ end
55
+ return self if io.eof?
56
+
57
+ self[:type].read io.read(4)
58
+ self[:block_len].read io.read(4)
59
+ self[:body].read io.read(self[:block_len].to_i - MIN_SIZE)
60
+ self[:block_len2].read io.read(4)
61
+
62
+ unless self[:block_len].to_i == self[:block_len2].to_i
63
+ raise InvalidFileError, 'Incoherency in Header Block'
64
+ end
65
+
66
+ self
67
+ end
68
+
69
+ # Return the object as a String
70
+ # @return [String]
71
+ def to_s
72
+ pad_field :body
73
+ recalc_block_len
74
+ to_a.map(&:to_s).join
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end