packetgen 1.2.0 → 1.3.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 +4 -4
- data/README.md +2 -2
- data/lib/packetgen/header/arp.rb +54 -125
- data/lib/packetgen/header/base.rb +175 -0
- data/lib/packetgen/header/dns/name.rb +110 -0
- data/lib/packetgen/header/dns/opt.rb +137 -0
- data/lib/packetgen/header/dns/option.rb +17 -0
- data/lib/packetgen/header/dns/qdsection.rb +39 -0
- data/lib/packetgen/header/dns/question.rb +129 -0
- data/lib/packetgen/header/dns/rr.rb +89 -0
- data/lib/packetgen/header/dns/rrsection.rb +72 -0
- data/lib/packetgen/header/dns.rb +276 -0
- data/lib/packetgen/header/esp.rb +38 -70
- data/lib/packetgen/header/eth.rb +35 -106
- data/lib/packetgen/header/icmp.rb +19 -70
- data/lib/packetgen/header/icmpv6.rb +3 -3
- data/lib/packetgen/header/ip.rb +54 -210
- data/lib/packetgen/header/ipv6.rb +73 -164
- data/lib/packetgen/header/tcp/option.rb +34 -50
- data/lib/packetgen/header/tcp/options.rb +19 -20
- data/lib/packetgen/header/tcp.rb +66 -129
- data/lib/packetgen/header/udp.rb +31 -88
- data/lib/packetgen/header.rb +5 -10
- data/lib/packetgen/inspect.rb +5 -4
- data/lib/packetgen/packet.rb +74 -57
- data/lib/packetgen/pcapng/block.rb +49 -7
- data/lib/packetgen/pcapng/epb.rb +36 -34
- data/lib/packetgen/pcapng/file.rb +24 -8
- data/lib/packetgen/pcapng/idb.rb +28 -33
- data/lib/packetgen/pcapng/shb.rb +35 -39
- data/lib/packetgen/pcapng/spb.rb +18 -27
- data/lib/packetgen/pcapng/unknown_block.rb +11 -21
- data/lib/packetgen/pcapng.rb +9 -7
- data/lib/packetgen/types/array.rb +56 -0
- data/lib/packetgen/types/fields.rb +325 -0
- data/lib/packetgen/types/int.rb +164 -0
- data/lib/packetgen/types/int_string.rb +69 -0
- data/lib/packetgen/types/string.rb +36 -0
- data/lib/packetgen/types/tlv.rb +41 -0
- data/lib/packetgen/types.rb +13 -0
- data/lib/packetgen/version.rb +1 -1
- data/lib/packetgen.rb +1 -1
- metadata +19 -6
- data/lib/packetgen/header/header_class_methods.rb +0 -106
- data/lib/packetgen/header/header_methods.rb +0 -73
- data/lib/packetgen/structfu.rb +0 -363
data/lib/packetgen/pcapng/shb.rb
CHANGED
@@ -17,10 +17,7 @@ module PacketGen
|
|
17
17
|
# Int64 :section_len
|
18
18
|
# String :options Default: ''
|
19
19
|
# Int32 :block_len2
|
20
|
-
class SHB <
|
21
|
-
:section_len, :options, :block_len2)
|
22
|
-
include StructFu
|
23
|
-
include Block
|
20
|
+
class SHB < Block
|
24
21
|
|
25
22
|
# @return [:little, :big]
|
26
23
|
attr_accessor :endian
|
@@ -43,6 +40,27 @@ module PacketGen
|
|
43
40
|
# +section_len+ value for undefined length
|
44
41
|
SECTION_LEN_UNDEFINED = 0xffffffff_ffffffff
|
45
42
|
|
43
|
+
# @!attribute magic
|
44
|
+
# 32-bit magic number
|
45
|
+
# @return [Integer]
|
46
|
+
define_field_before :block_len2, :magic, Types::Int32, default: MAGIC_INT32
|
47
|
+
# @!attribute ver_major
|
48
|
+
# 16-bit major version number
|
49
|
+
# @return [Integer]
|
50
|
+
define_field_before :block_len2, :ver_major, Types::Int16, default: 1
|
51
|
+
# @!attribute ver_major
|
52
|
+
# 16-bit minor version number
|
53
|
+
# @return [Integer]
|
54
|
+
define_field_before :block_len2, :ver_minor, Types::Int16, default: 0
|
55
|
+
# @!attribute section_len
|
56
|
+
# 64-bit section length
|
57
|
+
# @return [Integer]
|
58
|
+
define_field_before :block_len2, :section_len, Types::Int64,
|
59
|
+
default: SECTION_LEN_UNDEFINED
|
60
|
+
# @!attribute options
|
61
|
+
# @return [Types::String]
|
62
|
+
define_field_before :block_len2, :options, Types::String
|
63
|
+
|
46
64
|
# @param [Hash] options
|
47
65
|
# @option options [:little, :big] :endian set block endianness
|
48
66
|
# @option options [Integer] :type
|
@@ -58,28 +76,12 @@ module PacketGen
|
|
58
76
|
# @option options [::String] :options
|
59
77
|
# @option options [Integer] :block_len2 block total length
|
60
78
|
def initialize(options={})
|
61
|
-
|
79
|
+
super
|
62
80
|
@interfaces = []
|
63
81
|
@unknown_blocks = []
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
# Used by {#initialize} to set the initial fields
|
70
|
-
# @see #initialize possible options
|
71
|
-
# @param [Hash] options
|
72
|
-
# @return [Hash] return +options+
|
73
|
-
def init_fields(options={})
|
74
|
-
options[:type] = @int32.new(options[:type] || PcapNG::SHB_TYPE.to_i)
|
75
|
-
options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
|
76
|
-
options[:magic] = @int32.new(options[:magic] || MAGIC_INT32)
|
77
|
-
options[:ver_major] = @int16.new(options[:ver_major] || 1)
|
78
|
-
options[:ver_minor] = @int16.new(options[:ver_minor] || 0)
|
79
|
-
options[:section_len] = @int64.new(options[:section_len] || SECTION_LEN_UNDEFINED)
|
80
|
-
options[:options] = StructFu::String.new(options[:options] || '')
|
81
|
-
options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
|
82
|
-
options
|
82
|
+
set_endianness(options[:endian] || :little)
|
83
|
+
recalc_block_len
|
84
|
+
self.type = options[:type] || PcapNG::SHB_TYPE.to_i
|
83
85
|
end
|
84
86
|
|
85
87
|
# Reads a String or a IO to populate the object
|
@@ -130,10 +132,7 @@ module PacketGen
|
|
130
132
|
self[:options].read io.read(self[:block_len].to_i - MIN_SIZE)
|
131
133
|
self[:block_len2].read io.read(4)
|
132
134
|
|
133
|
-
|
134
|
-
raise InvalidFileError, 'Incoherency in Section Header Block'
|
135
|
-
end
|
136
|
-
|
135
|
+
check_len_coherency
|
137
136
|
self
|
138
137
|
end
|
139
138
|
|
@@ -154,25 +153,22 @@ module PacketGen
|
|
154
153
|
end
|
155
154
|
pad_field :options
|
156
155
|
recalc_block_len
|
157
|
-
|
156
|
+
fields.map { |f| @fields[f].to_s }.join + body
|
158
157
|
end
|
159
158
|
|
160
159
|
|
161
160
|
private
|
162
161
|
|
163
162
|
def force_endianness(endian)
|
164
|
-
set_endianness endian
|
165
163
|
@endian = endian
|
166
|
-
self[:type] =
|
167
|
-
self[:block_len] =
|
168
|
-
self[:magic] =
|
169
|
-
self[:ver_major] =
|
170
|
-
self[:ver_minor] =
|
171
|
-
self[:section_len] =
|
172
|
-
self[:block_len2] =
|
164
|
+
self[:type] = Types::Int32.new(self[:type].to_i, endian)
|
165
|
+
self[:block_len] = Types::Int32.new(self[:block_len].to_i, endian)
|
166
|
+
self[:magic] = Types::Int32.new(self[:magic].to_i, endian)
|
167
|
+
self[:ver_major] = Types::Int16.new(self[:ver_major].to_i, endian)
|
168
|
+
self[:ver_minor] = Types::Int16.new(self[:ver_minor].to_i, endian)
|
169
|
+
self[:section_len] = Types::Int64.new(self[:section_len].to_i, endian)
|
170
|
+
self[:block_len2] = Types::Int32.new(self[:block_len2].to_i, endian)
|
173
171
|
end
|
174
|
-
|
175
172
|
end
|
176
|
-
|
177
173
|
end
|
178
174
|
end
|
data/lib/packetgen/pcapng/spb.rb
CHANGED
@@ -14,17 +14,23 @@ module PacketGen
|
|
14
14
|
# Int32 :orig_len
|
15
15
|
# String :data
|
16
16
|
# Int32 :block_len2
|
17
|
-
class SPB <
|
18
|
-
|
19
|
-
|
17
|
+
class SPB < Block
|
18
|
+
|
19
|
+
# Minimum SPB size
|
20
|
+
MIN_SIZE = 4*4
|
20
21
|
|
21
22
|
# @return [:little, :big]
|
22
23
|
attr_accessor :endian
|
23
24
|
# @return [IPB]
|
24
25
|
attr_accessor :interface
|
25
26
|
|
26
|
-
#
|
27
|
-
|
27
|
+
# @!attribute orig_len
|
28
|
+
# 32-bit original length
|
29
|
+
# @return [Integer]
|
30
|
+
define_field_before :block_len2, :orig_len, Types::Int32, default: 0
|
31
|
+
# @!attribute data
|
32
|
+
# @return [Types::String]
|
33
|
+
define_field_before :block_len2, :data, Types::String
|
28
34
|
|
29
35
|
# @param [Hash] options
|
30
36
|
# @option options [:little, :big] :endian set block endianness
|
@@ -36,23 +42,10 @@ module PacketGen
|
|
36
42
|
# @option options [::String] :options
|
37
43
|
# @option options [Integer] :block_len2 block total length
|
38
44
|
def initialize(options={})
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
# Used by {#initialize} to set the initial fields
|
46
|
-
# @param [Hash] options
|
47
|
-
# @see #initialize possible options
|
48
|
-
# @return [Hash] return +options+
|
49
|
-
def init_fields(options={})
|
50
|
-
options[:type] = @int32.new(options[:type] || PcapNG::SPB_TYPE.to_i)
|
51
|
-
options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
|
52
|
-
options[:orig_len] = @int32.new(options[:orig_len] || 0)
|
53
|
-
options[:data] = StructFu::String.new(options[:data] || '')
|
54
|
-
options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
|
55
|
-
options
|
45
|
+
super
|
46
|
+
set_endianness(options[:endian] || :little)
|
47
|
+
recalc_block_len
|
48
|
+
self.type = options[:type] || PcapNG::SPB_TYPE.to_i
|
56
49
|
end
|
57
50
|
|
58
51
|
# Has this block option?
|
@@ -87,10 +80,8 @@ module PacketGen
|
|
87
80
|
io.read data_pad_len
|
88
81
|
self[:block_len2].read io.read(4)
|
89
82
|
|
90
|
-
|
91
|
-
|
92
|
-
end
|
93
|
-
|
83
|
+
check_len_coherency
|
84
|
+
self.type = self[:type] || PcapNG::IDB_TYPE.to_i
|
94
85
|
self
|
95
86
|
end
|
96
87
|
|
@@ -99,7 +90,7 @@ module PacketGen
|
|
99
90
|
def to_s
|
100
91
|
pad_field :data
|
101
92
|
recalc_block_len
|
102
|
-
|
93
|
+
fields.map { |f| @fields[f].to_s }.join
|
103
94
|
end
|
104
95
|
|
105
96
|
end
|
@@ -7,17 +7,19 @@ module PacketGen
|
|
7
7
|
module PcapNG
|
8
8
|
|
9
9
|
# {UnknownBlock} is used to handle unsupported blocks of a pcapng file.
|
10
|
-
class UnknownBlock <
|
11
|
-
|
12
|
-
|
10
|
+
class UnknownBlock < Block
|
11
|
+
|
12
|
+
# Minimum Iblock size
|
13
|
+
MIN_SIZE = 12
|
13
14
|
|
14
15
|
# @return [:little, :big]
|
15
16
|
attr_accessor :endian
|
16
17
|
# @return [SHB]
|
17
18
|
attr_accessor :section
|
18
19
|
|
19
|
-
#
|
20
|
-
|
20
|
+
# @!attribute body
|
21
|
+
# @return [Types::String]
|
22
|
+
define_field_before :block_len2, :body, Types::String
|
21
23
|
|
22
24
|
# @option options [:little, :big] :endian set block endianness
|
23
25
|
# @option options [Integer] :type
|
@@ -25,21 +27,9 @@ module PacketGen
|
|
25
27
|
# @option options [::String] :body
|
26
28
|
# @option options [Integer] :block_len2 block total length
|
27
29
|
def initialize(options={})
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
# Used by {#initialize} to set the initial fields
|
34
|
-
# @see #initialize possible options
|
35
|
-
# @param [Hash] options
|
36
|
-
# @return [Hash] return +options+
|
37
|
-
def init_fields(options={})
|
38
|
-
options[:type] = @int32.new(options[:type] || 0)
|
39
|
-
options[:block_len] = @int32.new(options[:block_len] || MIN_SIZE)
|
40
|
-
options[:body] = StructFu::String.new(options[:body] || '')
|
41
|
-
options[:block_len2] = @int32.new(options[:block_len2] || MIN_SIZE)
|
42
|
-
options
|
30
|
+
super
|
31
|
+
set_endianness(options[:endian] || :little)
|
32
|
+
recalc_block_len
|
43
33
|
end
|
44
34
|
|
45
35
|
# Has this block option?
|
@@ -76,7 +66,7 @@ module PacketGen
|
|
76
66
|
def to_s
|
77
67
|
pad_field :body
|
78
68
|
recalc_block_len
|
79
|
-
|
69
|
+
@fields.values.map(&:to_s).join
|
80
70
|
end
|
81
71
|
|
82
72
|
end
|
data/lib/packetgen/pcapng.rb
CHANGED
@@ -11,18 +11,20 @@ module PacketGen
|
|
11
11
|
module PcapNG
|
12
12
|
|
13
13
|
# Section Header Block type number
|
14
|
-
SHB_TYPE =
|
14
|
+
SHB_TYPE = Types::Int32.new(0x0A0D0D0A, :little)
|
15
15
|
# Interface Description Block type number
|
16
|
-
IDB_TYPE =
|
16
|
+
IDB_TYPE = Types::Int32.new(1, :little)
|
17
17
|
# Simple Packet Block type number
|
18
|
-
SPB_TYPE =
|
18
|
+
SPB_TYPE = Types::Int32.new(3, :little)
|
19
19
|
# Enhanced Packet Block type number
|
20
|
-
EPB_TYPE =
|
20
|
+
EPB_TYPE = Types::Int32.new(6, :little)
|
21
21
|
|
22
|
-
#
|
23
|
-
# FIXME: only ETHERNET type is defined as this is the only link layer
|
24
|
-
# type supported by PacketGen
|
22
|
+
# IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up)
|
25
23
|
LINKTYPE_ETHERNET = 1
|
24
|
+
# Raw IPv4; the packet begins with an IPv4 header.
|
25
|
+
LINKTYPE_IPV4 = 228
|
26
|
+
# Raw IPv6; the packet begins with an IPv6 header.
|
27
|
+
LINKTYPE_IPV6 = 229
|
26
28
|
|
27
29
|
# Base error class for PcapNG
|
28
30
|
class Error < PacketGen::Error; end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# This file is part of PacketGen
|
2
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
3
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
|
+
# This program is published under MIT license.
|
5
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Types
|
8
|
+
|
9
|
+
# @abstract
|
10
|
+
# Array supporting some fields methods
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
class Array < ::Array
|
13
|
+
|
14
|
+
# @abstract depend on private method +#record_from_hash+ which should be
|
15
|
+
# declared by subclasses.
|
16
|
+
# Add an object to this array
|
17
|
+
# @param [Object] obj type depends on subclass
|
18
|
+
# @return [Array] self
|
19
|
+
def push(obj)
|
20
|
+
obj = case obj
|
21
|
+
when Hash
|
22
|
+
record_from_hash obj
|
23
|
+
else
|
24
|
+
obj
|
25
|
+
end
|
26
|
+
super(obj)
|
27
|
+
end
|
28
|
+
alias :<< :push
|
29
|
+
|
30
|
+
# Get binary string
|
31
|
+
# @return [String]
|
32
|
+
def to_s
|
33
|
+
map(&:to_s).join
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get a human readable string
|
37
|
+
# @return [String]
|
38
|
+
def to_human
|
39
|
+
map(&:to_human).join(',')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get size in bytes
|
43
|
+
# @return [Integer]
|
44
|
+
def sz
|
45
|
+
to_s.size
|
46
|
+
end
|
47
|
+
|
48
|
+
# Force binary encoding for +str+
|
49
|
+
# @param [String] str
|
50
|
+
# @return [String] binary encoded string
|
51
|
+
def force_binary(str)
|
52
|
+
PacketGen.force_binary str
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
# This file is part of PacketGen
|
2
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
3
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
4
|
+
# This program is published under MIT license.
|
5
|
+
|
6
|
+
module PacketGen
|
7
|
+
module Types
|
8
|
+
|
9
|
+
# @abstract
|
10
|
+
# Set of fields
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
class Fields
|
13
|
+
|
14
|
+
# @private
|
15
|
+
@ordered_fields = []
|
16
|
+
# @private
|
17
|
+
@field_defs = {}
|
18
|
+
# @private
|
19
|
+
@bit_fields = []
|
20
|
+
|
21
|
+
# On inheritage, create +@field_defs+ class variable
|
22
|
+
# @param [Class] klass
|
23
|
+
# @return [void]
|
24
|
+
def self.inherited(klass)
|
25
|
+
ordered = @ordered_fields.clone
|
26
|
+
field_defs = @field_defs.clone
|
27
|
+
bf = @bit_fields.clone
|
28
|
+
klass.class_eval do
|
29
|
+
@ordered_fields = ordered
|
30
|
+
@field_defs = field_defs
|
31
|
+
@bit_fields = bf
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Define a field in class
|
36
|
+
# class BinaryStruct < PacketGen::Types::Fields
|
37
|
+
# # 8-bit value
|
38
|
+
# define_field :value1, Types::Int8
|
39
|
+
# # 16-bit value
|
40
|
+
# define_field :value2, Types::Int16
|
41
|
+
# # specific class, may use a specific constructor
|
42
|
+
# define_field :value3, MyClass, builder: ->(obj) { Myclass.new(obj) }
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# bs = BinaryStruct.new
|
46
|
+
# bs[value1] # => Types::Int8
|
47
|
+
# bs.value1 # => Integer
|
48
|
+
# @param [Symbol] name field name
|
49
|
+
# @param [Object] type class or instance
|
50
|
+
# @param [Hash] options Unrecognized options are passed to object builder if
|
51
|
+
# +:builder+ option is not set.
|
52
|
+
# @option options [Object] :default default value
|
53
|
+
# @option options [Lambda] :builder lambda to construct this field.
|
54
|
+
# Parameter to this lambda is the caller object.
|
55
|
+
# @return [void]
|
56
|
+
def self.define_field(name, type, options={})
|
57
|
+
define = []
|
58
|
+
if type < Types::Int
|
59
|
+
define << "def #{name}; self[:#{name}].to_i; end"
|
60
|
+
define << "def #{name}=(val) self[:#{name}].read val; end"
|
61
|
+
elsif type.instance_methods.include? :to_human and
|
62
|
+
type.instance_methods.include? :from_human
|
63
|
+
define << "def #{name}; self[:#{name}].to_human; end"
|
64
|
+
define << "def #{name}=(val) self[:#{name}].from_human val; end"
|
65
|
+
else
|
66
|
+
define << "def #{name}; self[:#{name}]; end\n"
|
67
|
+
define << "def #{name}=(val) self[:#{name}].read val; end"
|
68
|
+
end
|
69
|
+
|
70
|
+
define.delete(1) if type.instance_methods.include? "#{name}=".to_sym
|
71
|
+
define.delete(0) if type.instance_methods.include? name
|
72
|
+
class_eval define.join("\n")
|
73
|
+
@field_defs[name] = [type, options.delete(:default), options.delete(:builder),
|
74
|
+
options]
|
75
|
+
@ordered_fields << name
|
76
|
+
end
|
77
|
+
|
78
|
+
# Define a field, before another one
|
79
|
+
# @param [Symbol,nil] other field name to create a new one before. If +nil+,
|
80
|
+
# new field is appended.
|
81
|
+
# @param [Symbol] name field name to create
|
82
|
+
# @param [Object] type class or instance
|
83
|
+
# @param [Hash] options See {.define_field}.
|
84
|
+
# @return [void]
|
85
|
+
# @see .define_field
|
86
|
+
def self.define_field_before(other, name, type, options={})
|
87
|
+
define_field name, type, options
|
88
|
+
unless other.nil?
|
89
|
+
@ordered_fields.delete name
|
90
|
+
idx = @ordered_fields.index(other)
|
91
|
+
raise ArgumentError, "unknown #{other} field" if idx.nil?
|
92
|
+
@ordered_fields[idx, 0] = name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Define a field, after another one
|
97
|
+
# @param [Symbol,nil] other field name to create a new one after. If +nil+,
|
98
|
+
# new field is appended.
|
99
|
+
# @param [Symbol] name field name to create
|
100
|
+
# @param [Object] type class or instance
|
101
|
+
# @param [Hash] options See {.define_field}.
|
102
|
+
# @return [void]
|
103
|
+
# @see .define_field
|
104
|
+
def self.define_field_after(other, name, type, options={})
|
105
|
+
define_field name, type, options
|
106
|
+
unless other.nil?
|
107
|
+
@ordered_fields.delete name
|
108
|
+
idx = @ordered_fields.index(other)
|
109
|
+
raise ArgumentError, "unknown #{other} field" if idx.nil?
|
110
|
+
@ordered_fields[idx+1, 0] = name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Define a bitfield on given attribute
|
115
|
+
# class MyHeader < PacketGen::Types::Fields
|
116
|
+
# define_field :flags, Types::Int16
|
117
|
+
# # define a bit field on :flag attribute:
|
118
|
+
# # flag1, flag2 and flag3 are 1-bit fields
|
119
|
+
# # type and stype are 3-bit fields. reserved is a 6-bit field
|
120
|
+
# define_bit_fields_on :flags, :flag1, :flag2, :flag3, :type, 3, :stype, 3, :reserved: 7
|
121
|
+
# end
|
122
|
+
# A bitfield of size 1 bit defines 2 methods:
|
123
|
+
# * +#field?+ which returns a Boolean,
|
124
|
+
# * +#field=+ which takes and returns a Boolean.
|
125
|
+
# A bitfield of more bits defines 2 methods:
|
126
|
+
# * +#field+ which returns an Integer,
|
127
|
+
# * +#field=+ which takes and returns an Integer.
|
128
|
+
# @param [Symbol] attr attribute name (attribute should be a {Types::Int}
|
129
|
+
# subclass)
|
130
|
+
# @param [Array] args list of bitfield names. Name may be followed
|
131
|
+
# by bitfield size. If no size is given, 1 bit is assumed.
|
132
|
+
# @return [void]
|
133
|
+
def self.define_bit_fields_on(attr, *args)
|
134
|
+
attr_def = @field_defs[attr]
|
135
|
+
raise ArgumentError, "unknown #{attr} field" if attr_def.nil?
|
136
|
+
type = attr_def.first
|
137
|
+
unless type < Types::Int
|
138
|
+
raise TypeError, "#{attr} is not a PacketGen::Types::Int"
|
139
|
+
end
|
140
|
+
total_size = type.new.width * 8
|
141
|
+
idx = total_size - 1
|
142
|
+
|
143
|
+
field = args.shift
|
144
|
+
while field
|
145
|
+
next unless field.is_a? Symbol
|
146
|
+
size = if args.first.is_a? Integer
|
147
|
+
args.shift
|
148
|
+
else
|
149
|
+
1
|
150
|
+
end
|
151
|
+
unless field == :_
|
152
|
+
shift = idx - (size - 1)
|
153
|
+
field_mask = (2**size - 1) << shift
|
154
|
+
clear_mask = (2**total_size - 1) & (~field_mask & (2**total_size - 1))
|
155
|
+
|
156
|
+
if size == 1
|
157
|
+
class_eval <<-EOM
|
158
|
+
def #{field}?
|
159
|
+
val = (self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
160
|
+
val != 0
|
161
|
+
end
|
162
|
+
def #{field}=(v)
|
163
|
+
val = v ? 1 : 0
|
164
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
165
|
+
self[:#{attr}].value |= val << #{shift}
|
166
|
+
end
|
167
|
+
EOM
|
168
|
+
else
|
169
|
+
class_eval <<-EOM
|
170
|
+
def #{field}
|
171
|
+
(self[:#{attr}].to_i & #{field_mask}) >> #{shift}
|
172
|
+
end
|
173
|
+
def #{field}=(v)
|
174
|
+
self[:#{attr}].value = self[:#{attr}].to_i & #{clear_mask}
|
175
|
+
self[:#{attr}].value |= (v & #{2**size - 1}) << #{shift}
|
176
|
+
end
|
177
|
+
EOM
|
178
|
+
end
|
179
|
+
|
180
|
+
@bit_fields << field
|
181
|
+
end
|
182
|
+
|
183
|
+
idx -= size
|
184
|
+
field = args.shift
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Create a new header object
|
189
|
+
# @param [Hash] options Keys are symbols. They should have name of object
|
190
|
+
# attributes, as defined by {.define_field} and by {.define_bit_field}.
|
191
|
+
def initialize(options={})
|
192
|
+
@fields = {}
|
193
|
+
self.class.class_eval { @field_defs }.each do |field, ary|
|
194
|
+
default = ary[1].is_a?(Proc) ? ary[1].call : ary[1]
|
195
|
+
@fields[field] = if ary[2]
|
196
|
+
ary[2].call(self)
|
197
|
+
elsif !ary[3].empty?
|
198
|
+
ary[0].new(ary[3])
|
199
|
+
else
|
200
|
+
ary[0].new
|
201
|
+
end
|
202
|
+
|
203
|
+
value = options[field] || default
|
204
|
+
if ary[0] < Types::Int
|
205
|
+
@fields[field].read(value)
|
206
|
+
elsif ary[0] <= Types::String
|
207
|
+
@fields[field].read(value)
|
208
|
+
else
|
209
|
+
@fields[field].from_human(value) if @fields[field].respond_to? :from_human
|
210
|
+
end
|
211
|
+
end
|
212
|
+
self.class.class_eval { @bit_fields }.each do |bit_field|
|
213
|
+
self.send "#{bit_field}=", options[bit_field] if options[bit_field]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Get field object
|
218
|
+
# @param [Symbol] field
|
219
|
+
# @return [Object]
|
220
|
+
def [](field)
|
221
|
+
@fields[field]
|
222
|
+
end
|
223
|
+
|
224
|
+
# Set field object
|
225
|
+
# @param [Symbol] field
|
226
|
+
# @param [Object] obj
|
227
|
+
# @return [Object]
|
228
|
+
def []=(field, obj)
|
229
|
+
@fields[field] = obj
|
230
|
+
end
|
231
|
+
|
232
|
+
# Get all field names
|
233
|
+
# @return [Array<Symbol>]
|
234
|
+
def fields
|
235
|
+
@ordered_fields ||= self.class.class_eval { @ordered_fields }
|
236
|
+
end
|
237
|
+
|
238
|
+
# Return header protocol name
|
239
|
+
# @return [String]
|
240
|
+
def protocol_name
|
241
|
+
self.class.to_s.sub(/.*::/, '')
|
242
|
+
end
|
243
|
+
|
244
|
+
# Populate object from a binary string
|
245
|
+
# @param [String] str
|
246
|
+
# @return [Fields] self
|
247
|
+
def read(str)
|
248
|
+
return self if str.nil?
|
249
|
+
force_binary str
|
250
|
+
start = 0
|
251
|
+
fields.each do |field|
|
252
|
+
if self[field].respond_to? :width
|
253
|
+
width = self[field].width
|
254
|
+
self[field].read str[start, width]
|
255
|
+
start += width
|
256
|
+
elsif self[field].respond_to? :sz
|
257
|
+
self[field].read str[start..-1]
|
258
|
+
size = self[field].sz
|
259
|
+
start += size
|
260
|
+
else
|
261
|
+
self[field].read str[start..-1]
|
262
|
+
start = str.size
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
self
|
267
|
+
end
|
268
|
+
|
269
|
+
# Common inspect method for headers
|
270
|
+
# @return [String]
|
271
|
+
def inspect
|
272
|
+
str = Inspect.dashed_line(self.class, 2)
|
273
|
+
@fields.each do |attr, value|
|
274
|
+
next if attr == :body
|
275
|
+
str << Inspect.inspect_attribute(attr, value, 2)
|
276
|
+
end
|
277
|
+
str
|
278
|
+
end
|
279
|
+
|
280
|
+
# Return object as a binary string
|
281
|
+
# @return [String]
|
282
|
+
def to_s
|
283
|
+
fields.map { |f| force_binary @fields[f].to_s }.join
|
284
|
+
end
|
285
|
+
|
286
|
+
# Size of object as binary strinf
|
287
|
+
# @return [nteger]
|
288
|
+
def sz
|
289
|
+
to_s.size
|
290
|
+
end
|
291
|
+
|
292
|
+
# Return object as a hash
|
293
|
+
# @return [Hash] keys: attributes, values: attribute values
|
294
|
+
def to_h
|
295
|
+
Hash[fields.map { |f| [f, @fields[f]] }]
|
296
|
+
end
|
297
|
+
|
298
|
+
# Used to set body as value of body object.
|
299
|
+
# @param [String,Int,Fields,nil] value
|
300
|
+
# @return [void]
|
301
|
+
# @raise [BodyError] no body on given object
|
302
|
+
# @raise [ArgumentError] cannot cram +body+ in +:body+ field
|
303
|
+
def body=(value)
|
304
|
+
raise BodyError, 'no body field' unless @fields.has_key? :body
|
305
|
+
case body
|
306
|
+
when ::String
|
307
|
+
self[:body].read value
|
308
|
+
when Int, Fields
|
309
|
+
self[:body] = value
|
310
|
+
when NilClass
|
311
|
+
self[:body] = Types::String.new.read('')
|
312
|
+
else
|
313
|
+
raise ArgumentError, "Can't cram a #{body.class} in a :body field"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Force str to binary encoding
|
318
|
+
# @param [String] str
|
319
|
+
# @return [String]
|
320
|
+
def force_binary(str)
|
321
|
+
PacketGen.force_binary(str)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|