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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +116 -0
- data/Rakefile +18 -0
- data/lib/packetgen.rb +83 -0
- data/lib/packetgen/capture.rb +105 -0
- data/lib/packetgen/header.rb +21 -0
- data/lib/packetgen/header/arp.rb +148 -0
- data/lib/packetgen/header/eth.rb +155 -0
- data/lib/packetgen/header/header_class_methods.rb +28 -0
- data/lib/packetgen/header/header_methods.rb +51 -0
- data/lib/packetgen/header/ip.rb +283 -0
- data/lib/packetgen/header/ipv6.rb +215 -0
- data/lib/packetgen/header/udp.rb +133 -0
- data/lib/packetgen/packet.rb +357 -0
- data/lib/packetgen/pcapng.rb +39 -0
- data/lib/packetgen/pcapng/block.rb +32 -0
- data/lib/packetgen/pcapng/epb.rb +131 -0
- data/lib/packetgen/pcapng/file.rb +345 -0
- data/lib/packetgen/pcapng/idb.rb +145 -0
- data/lib/packetgen/pcapng/shb.rb +173 -0
- data/lib/packetgen/pcapng/spb.rb +103 -0
- data/lib/packetgen/pcapng/unknown_block.rb +80 -0
- data/lib/packetgen/structfu.rb +357 -0
- data/lib/packetgen/version.rb +7 -0
- data/packetgen.gemspec +30 -0
- metadata +155 -0
@@ -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
|