packetgen 0.1.0

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.
@@ -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