packetgen 2.8.7 → 3.0.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/.rubocop.yml +0 -1
- data/README.md +5 -4
- data/lib/packetgen.rb +6 -12
- data/lib/packetgen/capture.rb +43 -39
- data/lib/packetgen/config.rb +0 -1
- data/lib/packetgen/deprecation.rb +1 -1
- data/lib/packetgen/header.rb +9 -9
- data/lib/packetgen/header/asn1_base.rb +10 -10
- data/lib/packetgen/header/base.rb +42 -101
- data/lib/packetgen/header/dhcp/option.rb +5 -11
- data/lib/packetgen/header/dhcpv6/duid.rb +2 -0
- data/lib/packetgen/header/dhcpv6/option.rb +2 -19
- data/lib/packetgen/header/dhcpv6/options.rb +7 -0
- data/lib/packetgen/header/dns.rb +5 -23
- data/lib/packetgen/header/dns/name.rb +1 -0
- data/lib/packetgen/header/dns/qdsection.rb +1 -0
- data/lib/packetgen/header/dns/question.rb +3 -7
- data/lib/packetgen/header/dns/rr.rb +3 -0
- data/lib/packetgen/header/dns/rrsection.rb +1 -0
- data/lib/packetgen/header/dot11.rb +1 -17
- data/lib/packetgen/header/dot1x.rb +1 -0
- data/lib/packetgen/header/eap.rb +4 -7
- data/lib/packetgen/header/eth.rb +2 -0
- data/lib/packetgen/header/http/headers.rb +3 -0
- data/lib/packetgen/header/http/request.rb +5 -4
- data/lib/packetgen/header/http/response.rb +5 -4
- data/lib/packetgen/header/icmp.rb +6 -0
- data/lib/packetgen/header/icmpv6.rb +6 -0
- data/lib/packetgen/header/igmpv3/mq.rb +2 -0
- data/lib/packetgen/header/ip.rb +32 -30
- data/lib/packetgen/header/ip/addr.rb +1 -0
- data/lib/packetgen/header/ip/option.rb +23 -20
- data/lib/packetgen/header/ip/options.rb +11 -24
- data/lib/packetgen/header/ipv6.rb +45 -34
- data/lib/packetgen/header/ipv6/addr.rb +2 -0
- data/lib/packetgen/header/ipv6/hop_by_hop.rb +7 -31
- data/lib/packetgen/header/mdns.rb +1 -0
- data/lib/packetgen/header/mldv2/mlq.rb +2 -0
- data/lib/packetgen/header/ospfv2/lsa.rb +15 -25
- data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +1 -1
- data/lib/packetgen/header/ospfv3/lsa.rb +8 -25
- data/lib/packetgen/header/snmp.rb +2 -0
- data/lib/packetgen/header/tcp.rb +23 -2
- data/lib/packetgen/header/tcp/option.rb +51 -52
- data/lib/packetgen/header/tcp/options.rb +17 -52
- data/lib/packetgen/header/tftp.rb +3 -0
- data/lib/packetgen/header/udp.rb +8 -0
- data/lib/packetgen/packet.rb +119 -102
- data/lib/packetgen/pcapng/block.rb +4 -10
- data/lib/packetgen/pcapng/epb.rb +4 -4
- data/lib/packetgen/pcapng/file.rb +7 -3
- data/lib/packetgen/pcapng/idb.rb +2 -2
- data/lib/packetgen/pcapng/shb.rb +3 -3
- data/lib/packetgen/pcapng/spb.rb +1 -8
- data/lib/packetgen/pcapng/unknown_block.rb +0 -7
- data/lib/packetgen/types.rb +1 -0
- data/lib/packetgen/types/array.rb +73 -71
- data/lib/packetgen/types/cstring.rb +1 -1
- data/lib/packetgen/types/enum.rb +3 -3
- data/lib/packetgen/types/fields.rb +66 -106
- data/lib/packetgen/types/int.rb +9 -5
- data/lib/packetgen/types/length_from.rb +45 -0
- data/lib/packetgen/types/oui.rb +2 -0
- data/lib/packetgen/types/string.rb +10 -16
- data/lib/packetgen/types/tlv.rb +7 -15
- data/lib/packetgen/utils.rb +8 -8
- data/lib/packetgen/utils/arp_spoofer.rb +1 -2
- data/lib/packetgen/version.rb +1 -1
- metadata +3 -21
- data/lib/packetgen/header/crypto.rb +0 -62
- data/lib/packetgen/header/esp.rb +0 -413
- data/lib/packetgen/header/ike.rb +0 -243
- data/lib/packetgen/header/ike/auth.rb +0 -165
- data/lib/packetgen/header/ike/cert.rb +0 -76
- data/lib/packetgen/header/ike/certreq.rb +0 -66
- data/lib/packetgen/header/ike/id.rb +0 -99
- data/lib/packetgen/header/ike/ke.rb +0 -79
- data/lib/packetgen/header/ike/nonce.rb +0 -40
- data/lib/packetgen/header/ike/notify.rb +0 -176
- data/lib/packetgen/header/ike/payload.rb +0 -315
- data/lib/packetgen/header/ike/sa.rb +0 -561
- data/lib/packetgen/header/ike/sk.rb +0 -261
- data/lib/packetgen/header/ike/ts.rb +0 -270
- data/lib/packetgen/header/ike/vendor_id.rb +0 -39
- data/lib/packetgen/header/netbios.rb +0 -20
- data/lib/packetgen/header/netbios/datagram.rb +0 -105
- data/lib/packetgen/header/netbios/name.rb +0 -67
- data/lib/packetgen/header/netbios/session.rb +0 -64
@@ -37,13 +37,6 @@ module PacketGen
|
|
37
37
|
@fields.key?(:options) && @fields[:options].sz > 0
|
38
38
|
end
|
39
39
|
|
40
|
-
# @deprecated Use {#options?} instead.
|
41
|
-
# @return [Boolean]
|
42
|
-
def has_options?
|
43
|
-
Deprecation.deprecated(self.class, __method__, 'options?')
|
44
|
-
options?
|
45
|
-
end
|
46
|
-
|
47
40
|
# Calculate block length and update :block_len and block_len2 fields
|
48
41
|
# @return [void]
|
49
42
|
def recalc_block_len
|
@@ -56,8 +49,8 @@ module PacketGen
|
|
56
49
|
# @return [void]
|
57
50
|
def pad_field(*fields)
|
58
51
|
fields.each do |field|
|
59
|
-
unless (@fields[field].
|
60
|
-
@fields[field] << "\x00" * (4 - (@fields[field].
|
52
|
+
unless (@fields[field].sz % 4).zero?
|
53
|
+
@fields[field] << "\x00" * (4 - (@fields[field].sz % 4))
|
61
54
|
end
|
62
55
|
end
|
63
56
|
end
|
@@ -72,13 +65,14 @@ module PacketGen
|
|
72
65
|
unless %i[little big].include? endian
|
73
66
|
raise ArgumentError, "unknown endianness for #{self.class}"
|
74
67
|
end
|
68
|
+
|
75
69
|
@endian = endian
|
76
70
|
@fields.each { |_f, v| v.endian = endian if v.is_a?(Types::Int) }
|
77
71
|
endian
|
78
72
|
end
|
79
73
|
|
80
74
|
def check_len_coherency
|
81
|
-
unless self
|
75
|
+
unless self.block_len == self.block_len2
|
82
76
|
raise InvalidFileError, 'Incoherency in Block length'
|
83
77
|
end
|
84
78
|
end
|
data/lib/packetgen/pcapng/epb.rb
CHANGED
@@ -96,10 +96,10 @@ module PacketGen
|
|
96
96
|
self[:tsl].read io.read(4)
|
97
97
|
self[:cap_len].read io.read(4)
|
98
98
|
self[:orig_len].read io.read(4)
|
99
|
-
self[:data].read io.read(self
|
99
|
+
self[:data].read io.read(self.cap_len)
|
100
100
|
data_pad_len = (4 - (self[:cap_len].to_i % 4)) % 4
|
101
101
|
io.read data_pad_len
|
102
|
-
options_len = self
|
102
|
+
options_len = self.block_len - self.cap_len - data_pad_len
|
103
103
|
options_len -= MIN_SIZE
|
104
104
|
self[:options].read io.read(options_len)
|
105
105
|
self[:block_len2].read io.read(4)
|
@@ -111,7 +111,7 @@ module PacketGen
|
|
111
111
|
# Return timestamp as a Time object
|
112
112
|
# @return [Time]
|
113
113
|
def timestamp
|
114
|
-
Time.at((self
|
114
|
+
Time.at((self.tsh << 32 | self.tsl) * ts_resol)
|
115
115
|
end
|
116
116
|
|
117
117
|
# Return the object as a String
|
@@ -125,7 +125,7 @@ module PacketGen
|
|
125
125
|
private
|
126
126
|
|
127
127
|
def ts_resol
|
128
|
-
if @interface.nil?
|
128
|
+
if !defined?(@interface) || @interface.nil?
|
129
129
|
1E-6
|
130
130
|
else
|
131
131
|
@interface.ts_resol
|
@@ -66,10 +66,14 @@ module PacketGen
|
|
66
66
|
end
|
67
67
|
|
68
68
|
return unless blk
|
69
|
+
|
69
70
|
count = 0
|
70
71
|
@sections.each do |section|
|
71
72
|
section.interfaces.each do |intf|
|
72
|
-
intf.packets.each
|
73
|
+
intf.packets.each do |pkt|
|
74
|
+
count += 1
|
75
|
+
yield pkt
|
76
|
+
end
|
73
77
|
end
|
74
78
|
end
|
75
79
|
count
|
@@ -347,8 +351,8 @@ module PacketGen
|
|
347
351
|
shb << block
|
348
352
|
block.section = shb
|
349
353
|
when EPB
|
350
|
-
shb.interfaces[block.interface_id
|
351
|
-
block.interface = shb.interfaces[block.interface_id
|
354
|
+
shb.interfaces[block.interface_id] << block
|
355
|
+
block.interface = shb.interfaces[block.interface_id]
|
352
356
|
when SPB
|
353
357
|
shb.interfaces[0] << block
|
354
358
|
block.interface = shb.interfaces[0]
|
data/lib/packetgen/pcapng/idb.rb
CHANGED
@@ -83,7 +83,7 @@ module PacketGen
|
|
83
83
|
self[:link_type].read io.read(2)
|
84
84
|
self[:reserved].read io.read(2)
|
85
85
|
self[:snaplen].read io.read(4)
|
86
|
-
self[:options].read io.read(self
|
86
|
+
self[:options].read io.read(self.block_len - MIN_SIZE)
|
87
87
|
self[:block_len2].read io.read(4)
|
88
88
|
|
89
89
|
check_len_coherency
|
@@ -105,7 +105,7 @@ module PacketGen
|
|
105
105
|
if @options_decoded && !force
|
106
106
|
@ts_resol
|
107
107
|
else
|
108
|
-
packstr =
|
108
|
+
packstr = endian == :little ? 'v' : 'n'
|
109
109
|
idx = 0
|
110
110
|
options = self[:options]
|
111
111
|
|
data/lib/packetgen/pcapng/shb.rb
CHANGED
@@ -130,7 +130,7 @@ module PacketGen
|
|
130
130
|
self[:ver_major].read io.read(2)
|
131
131
|
self[:ver_minor].read io.read(2)
|
132
132
|
self[:section_len].read io.read(8)
|
133
|
-
self[:options].read io.read(self
|
133
|
+
self[:options].read io.read(self.block_len - MIN_SIZE)
|
134
134
|
self[:block_len2].read io.read(4)
|
135
135
|
|
136
136
|
check_len_coherency
|
@@ -149,8 +149,8 @@ module PacketGen
|
|
149
149
|
# @return [String]
|
150
150
|
def to_s
|
151
151
|
body = @interfaces.map(&:to_s).join
|
152
|
-
unless self
|
153
|
-
self.section_len
|
152
|
+
unless self.section_len == SECTION_LEN_UNDEFINED
|
153
|
+
self.section_len = body.size
|
154
154
|
end
|
155
155
|
pad_field :options
|
156
156
|
recalc_block_len
|
data/lib/packetgen/pcapng/spb.rb
CHANGED
@@ -56,13 +56,6 @@ module PacketGen
|
|
56
56
|
false
|
57
57
|
end
|
58
58
|
|
59
|
-
# @deprecated Use {#options?} instead.
|
60
|
-
# @return [false]
|
61
|
-
def has_options?
|
62
|
-
Deprecation.deprecated(self.class, __method__, 'options?')
|
63
|
-
options?
|
64
|
-
end
|
65
|
-
|
66
59
|
# Reads a String or a IO to populate the object
|
67
60
|
# @param [::String,IO] str_or_io
|
68
61
|
# @return [self]
|
@@ -90,7 +83,7 @@ module PacketGen
|
|
90
83
|
self[:block_len2].read io.read(4)
|
91
84
|
|
92
85
|
check_len_coherency
|
93
|
-
self.type
|
86
|
+
self.type ||= PcapNG::IDB_TYPE.to_i
|
94
87
|
self
|
95
88
|
end
|
96
89
|
|
@@ -40,13 +40,6 @@ module PacketGen
|
|
40
40
|
false
|
41
41
|
end
|
42
42
|
|
43
|
-
# @deprecated Use {#options?} instead.
|
44
|
-
# @return [false]
|
45
|
-
def has_options?
|
46
|
-
Deprecation.deprecated(self.class, __method__, 'options?')
|
47
|
-
options?
|
48
|
-
end
|
49
|
-
|
50
43
|
# Reads a String or a IO to populate the object
|
51
44
|
# @param [::String,IO] str_or_io
|
52
45
|
# @return [self]
|
data/lib/packetgen/types.rb
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
# frozen_string_literal: true
|
7
7
|
|
8
|
+
require 'forwardable'
|
9
|
+
|
8
10
|
module PacketGen
|
9
11
|
module Types
|
10
12
|
# @abstract Base class to define set of {Fields} subclasses.
|
@@ -14,20 +16,66 @@ module PacketGen
|
|
14
16
|
#
|
15
17
|
# A default method is defined by {Array}: it calls constructor of class defined
|
16
18
|
# by {.set_of}.
|
19
|
+
#
|
20
|
+
# == #real_type
|
21
|
+
# Subclasses should define private method +#real_type+ is {.set_of} type
|
22
|
+
# may be subclassed. This method should return real class to use. It
|
23
|
+
# takes an only argument, which is of type given by {.set_of}.
|
24
|
+
#
|
25
|
+
# Default behaviour of this method is to argument's class.
|
26
|
+
#
|
17
27
|
# @author Sylvain Daubert
|
18
28
|
class Array
|
29
|
+
extend Forwardable
|
30
|
+
|
31
|
+
# @!method [](index)
|
32
|
+
# Return the element at +index+.
|
33
|
+
# @param [integer] index
|
34
|
+
# @return [Object]
|
35
|
+
# @!method clear
|
36
|
+
# Clear array.
|
37
|
+
# @return [void]
|
38
|
+
# @!method each
|
39
|
+
# Calls the given block once for each element in self, passing that
|
40
|
+
# element as a parameter. Returns the array itself.
|
41
|
+
# @return [Array]
|
42
|
+
# @method empty?
|
43
|
+
# Return +true+ if contains no element.
|
44
|
+
# @return [Booelan]
|
45
|
+
# @!method first
|
46
|
+
# Return first element
|
47
|
+
# @return [Object]
|
48
|
+
# @!method last
|
49
|
+
# Return last element.
|
50
|
+
# @return [Object]
|
51
|
+
# @!method size
|
52
|
+
# Get number of element in array
|
53
|
+
# @return [Integer]
|
54
|
+
def_delegators :@array, :[], :clear, :each, :empty?, :first, :last, :size
|
55
|
+
alias length size
|
56
|
+
|
19
57
|
include Enumerable
|
58
|
+
include LengthFrom
|
20
59
|
|
21
60
|
# Separator used in {#to_human}.
|
22
61
|
# May be ovverriden by subclasses
|
23
62
|
HUMAN_SEPARATOR = ','
|
24
63
|
|
25
|
-
# Define type of objects in set. Used by {#read} and {#push}.
|
26
|
-
# @param [Class] klass
|
27
|
-
# @return [void]
|
28
64
|
# rubocop:disable Naming/AccessorMethodName
|
29
|
-
|
30
|
-
|
65
|
+
class <<self
|
66
|
+
# Get class set with {.set_of}.
|
67
|
+
# @return [Class]
|
68
|
+
# @since 3.0.0
|
69
|
+
def set_of_klass
|
70
|
+
@klass
|
71
|
+
end
|
72
|
+
|
73
|
+
# Define type of objects in set. Used by {#read} and {#push}.
|
74
|
+
# @param [Class] klass
|
75
|
+
# @return [void]
|
76
|
+
def set_of(klass)
|
77
|
+
@klass = klass
|
78
|
+
end
|
31
79
|
end
|
32
80
|
# rubocop:enable Naming/AccessorMethodName
|
33
81
|
|
@@ -36,6 +84,7 @@ module PacketGen
|
|
36
84
|
def initialize(options={})
|
37
85
|
@counter = options[:counter]
|
38
86
|
@array = []
|
87
|
+
initialize_length_from(options)
|
39
88
|
end
|
40
89
|
|
41
90
|
# Initialize array for copy:
|
@@ -44,26 +93,13 @@ module PacketGen
|
|
44
93
|
@array = @array.dup
|
45
94
|
end
|
46
95
|
|
47
|
-
# Return the element at +index+.
|
48
|
-
# @param [integer] index
|
49
|
-
# @return [Object]
|
50
|
-
def [](index)
|
51
|
-
@array[index]
|
52
|
-
end
|
53
|
-
|
54
96
|
def ==(other)
|
55
|
-
case other
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
# Clear array.
|
64
|
-
# @return [void]
|
65
|
-
def clear
|
66
|
-
@array.clear
|
97
|
+
@array == case other
|
98
|
+
when Array
|
99
|
+
other.to_a
|
100
|
+
else
|
101
|
+
other
|
102
|
+
end
|
67
103
|
end
|
68
104
|
|
69
105
|
# Clear array. Reset associated counter, if any.
|
@@ -91,31 +127,6 @@ module PacketGen
|
|
91
127
|
deleted
|
92
128
|
end
|
93
129
|
|
94
|
-
# Calls the given block once for each element in self, passing that
|
95
|
-
# element as a parameter. Returns the array itself.
|
96
|
-
# @return [Array]
|
97
|
-
def each
|
98
|
-
@array.each { |el| yield el }
|
99
|
-
end
|
100
|
-
|
101
|
-
# Return +true+ if contains no element.
|
102
|
-
# @return [Booelan]
|
103
|
-
def empty?
|
104
|
-
@array.empty?
|
105
|
-
end
|
106
|
-
|
107
|
-
# Return first element
|
108
|
-
# @return [Object]
|
109
|
-
def first
|
110
|
-
@array.first
|
111
|
-
end
|
112
|
-
|
113
|
-
# Return last element.
|
114
|
-
# @return [Object]
|
115
|
-
def last
|
116
|
-
@array.last
|
117
|
-
end
|
118
|
-
|
119
130
|
# @abstract depend on private method +#record_from_hash+ which should be
|
120
131
|
# declared by subclasses.
|
121
132
|
# Add an object to this array
|
@@ -150,10 +161,13 @@ module PacketGen
|
|
150
161
|
clear
|
151
162
|
return self if str.nil?
|
152
163
|
return self if @counter && @counter.to_i.zero?
|
153
|
-
|
154
|
-
|
164
|
+
|
165
|
+
str = read_with_length_from(str)
|
166
|
+
klass = self.class.set_of_klass
|
155
167
|
until str.empty?
|
156
168
|
obj = klass.new.read(str)
|
169
|
+
real_klass = real_type(obj)
|
170
|
+
obj = real_klass.new.read(str) unless real_klass == klass
|
157
171
|
@array << obj
|
158
172
|
str.slice!(0, obj.sz)
|
159
173
|
break if @counter && self.size == @counter.to_i
|
@@ -161,13 +175,6 @@ module PacketGen
|
|
161
175
|
self
|
162
176
|
end
|
163
177
|
|
164
|
-
# Get number of element in array
|
165
|
-
# @return [Integer]
|
166
|
-
def size
|
167
|
-
@array.size
|
168
|
-
end
|
169
|
-
alias length size
|
170
|
-
|
171
178
|
# Get size in bytes
|
172
179
|
# @return [Integer]
|
173
180
|
def sz
|
@@ -192,22 +199,17 @@ module PacketGen
|
|
192
199
|
@array.map(&:to_human).join(self.class::HUMAN_SEPARATOR)
|
193
200
|
end
|
194
201
|
|
195
|
-
# Force binary encoding for +str+
|
196
|
-
# @param [String] str
|
197
|
-
# @return [String] binary encoded string
|
198
|
-
def force_binary(str)
|
199
|
-
PacketGen.force_binary str
|
200
|
-
end
|
201
|
-
|
202
202
|
private
|
203
203
|
|
204
204
|
def record_from_hash(obj)
|
205
|
-
obj_klass = self.class.
|
206
|
-
if obj_klass
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
205
|
+
obj_klass = self.class.set_of_klass
|
206
|
+
return obj_klass.new(obj) if obj_klass
|
207
|
+
|
208
|
+
raise NotImplementedError, 'class should define #record_from_hash or declare type of elements in set with .set_of'
|
209
|
+
end
|
210
|
+
|
211
|
+
def real_type(obj)
|
212
|
+
obj.class
|
211
213
|
end
|
212
214
|
end
|
213
215
|
|
@@ -32,7 +32,7 @@ module PacketGen
|
|
32
32
|
# get null-terminated string
|
33
33
|
# @return [String]
|
34
34
|
def to_s
|
35
|
-
if @static_length.is_a?
|
35
|
+
if defined?(@static_length) && @static_length.is_a?(Integer)
|
36
36
|
if self.size >= @static_length
|
37
37
|
s = self[0, @static_length]
|
38
38
|
s[-1] = "\x00".encode(s.encoding)
|
data/lib/packetgen/types/enum.rb
CHANGED
@@ -45,9 +45,10 @@ module PacketGen
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Setter for value attribute
|
48
|
-
# @param [
|
48
|
+
# @param [#to_i, String,nil] value value as an Integer or as a String
|
49
49
|
# from enumration
|
50
50
|
# @return [Integer]
|
51
|
+
# @raise [ArgumentError] String value is unknown
|
51
52
|
def value=(value)
|
52
53
|
ival = case value
|
53
54
|
when NilClass
|
@@ -56,8 +57,7 @@ module PacketGen
|
|
56
57
|
raise ArgumentError, "#{value.inspect} not in enumeration" unless @enum.key? value
|
57
58
|
@enum[value]
|
58
59
|
else
|
59
|
-
|
60
|
-
value
|
60
|
+
value.to_i
|
61
61
|
end
|
62
62
|
@value = ival
|
63
63
|
end
|
@@ -49,7 +49,7 @@ module PacketGen
|
|
49
49
|
# define_field :type, PacketGen::Types::Int16le
|
50
50
|
# # define a string field
|
51
51
|
# define_field :body, PacketGen::Types::String
|
52
|
-
# # define
|
52
|
+
# # define a field using a complex type (Fields subclass)
|
53
53
|
# define_field :mac_addr, PacketGen::Eth::MacAddr
|
54
54
|
#
|
55
55
|
# This example creates six methods on our Fields subclass: +#type+, +#type=+,
|
@@ -59,20 +59,22 @@ module PacketGen
|
|
59
59
|
# * +:default+ gives default field value. It may be a simple value (an Integer
|
60
60
|
# for an Int field, for example) or a lambda,
|
61
61
|
# * +:builder+ to give a builder/constructor lambda to create field. The lambda
|
62
|
-
# takes
|
62
|
+
# takes 2 argument: {Fields} subclass object owning field, and type class as passes
|
63
|
+
# as second argument to .define_field,
|
63
64
|
# * +:optional+ to define this field as optional. This option takes a lambda
|
64
|
-
# parameter used to say if this field is present or not
|
65
|
+
# parameter used to say if this field is present or not. The lambda takes an argument
|
66
|
+
# ({Fields} subclass object owning field),
|
65
67
|
# * +:enum+ to define Hash enumeration for an {Enum} type.
|
66
68
|
# For example:
|
67
69
|
# # 32-bit integer field defaulting to 1
|
68
70
|
# define_field :type, PacketGen::Types::Int32, default: 1
|
69
71
|
# # 16-bit integer field, created with a random value. Each instance of this
|
70
72
|
# # object will have a different value.
|
71
|
-
# define_field :id, PacketGen::Types::Int16, default: ->{ rand(65535) }
|
73
|
+
# define_field :id, PacketGen::Types::Int16, default: ->(obj) { rand(65535) }
|
72
74
|
# # a size field
|
73
75
|
# define_field :body_size, PacketGen::Types::Int16
|
74
76
|
# # String field which length is taken from body_size field
|
75
|
-
# define_field :body, PacketGen::Types::String, builder: ->(obj, type) { type.new(
|
77
|
+
# define_field :body, PacketGen::Types::String, builder: ->(obj, type) { type.new(length_from: obj[:body_size]) }
|
76
78
|
# # 16-bit enumeration type. As :default not specified, default to first value of enum
|
77
79
|
# define_field :type_class, PacketGen::Types::Int16Enum, enum: { 'class1' => 1, 'class2' => 2}
|
78
80
|
# # optional field. Only present if another field has a certain value
|
@@ -92,8 +94,19 @@ module PacketGen
|
|
92
94
|
# to access Boolean RSV, MF and DF flags from +frag+ field,
|
93
95
|
# * +#fragment_offset+ and +#fragment_offset=+ to access 13-bit integer fragment
|
94
96
|
# offset subfield from +frag+ field.
|
97
|
+
#
|
98
|
+
# == Creating a new field class from another one
|
99
|
+
# Some methods may help in this case:
|
100
|
+
# * {.define_field_before} to define a new field before an existing one,
|
101
|
+
# * {.define_field_after} to define a new field after an existing onr,
|
102
|
+
# * {.remove_field} to remove an existing field,
|
103
|
+
# * {.uptade_fied} to change options of a field (but not its type),
|
104
|
+
# * {.remove_bit_fields_on} to remove a bit fields definition.
|
105
|
+
#
|
95
106
|
# @author Sylvain Daubert
|
96
107
|
class Fields
|
108
|
+
# @private
|
109
|
+
FieldDef = Struct.new(:type, :default, :builder, :optional, :enum, :options)
|
97
110
|
# @private field names, ordered as they were declared
|
98
111
|
@ordered_fields = []
|
99
112
|
# @private field definitions
|
@@ -112,6 +125,7 @@ module PacketGen
|
|
112
125
|
end
|
113
126
|
ordered = @ordered_fields.clone
|
114
127
|
bf = @bit_fields.clone
|
128
|
+
|
115
129
|
klass.class_eval do
|
116
130
|
@ordered_fields = ordered
|
117
131
|
@field_defs = field_defs
|
@@ -157,26 +171,32 @@ module PacketGen
|
|
157
171
|
if type < Types::Enum
|
158
172
|
define << "def #{name}; self[:#{name}].to_i; end"
|
159
173
|
define << "def #{name}=(val) self[:#{name}].value = val; end"
|
160
|
-
elsif type < Types::Int
|
161
|
-
define << "def #{name}; self[:#{name}].to_i; end"
|
162
|
-
define << "def #{name}=(val) self[:#{name}].read val; end"
|
163
|
-
elsif type.instance_methods.include?(:to_human) &&
|
164
|
-
type.instance_methods.include?(:from_human)
|
165
|
-
define << "def #{name}; self[:#{name}].to_human; end"
|
166
|
-
define << "def #{name}=(val) self[:#{name}].from_human val; end"
|
167
174
|
else
|
168
|
-
define << "def #{name}
|
169
|
-
|
175
|
+
define << "def #{name}\n" \
|
176
|
+
" if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
|
177
|
+
" self[:#{name}].to_human\n" \
|
178
|
+
" else\n" \
|
179
|
+
" self[:#{name}]\n" \
|
180
|
+
" end\n" \
|
181
|
+
"end"
|
182
|
+
define << "def #{name}=(val)\n" \
|
183
|
+
" if self[:#{name}].respond_to?(:to_human) && self[:#{name}].respond_to?(:from_human)\n" \
|
184
|
+
" self[:#{name}].from_human val\n" \
|
185
|
+
" else\n" \
|
186
|
+
" self[:#{name}].read val\n" \
|
187
|
+
" end\n" \
|
188
|
+
"end"
|
170
189
|
end
|
171
190
|
|
172
191
|
define.delete(1) if type.instance_methods.include? "#{name}=".to_sym
|
173
192
|
define.delete(0) if type.instance_methods.include? name
|
174
193
|
class_eval define.join("\n")
|
175
|
-
@field_defs[name] =
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
194
|
+
@field_defs[name] = FieldDef.new(type,
|
195
|
+
options.delete(:default),
|
196
|
+
options.delete(:builder),
|
197
|
+
options.delete(:optional),
|
198
|
+
options.delete(:enum),
|
199
|
+
options)
|
180
200
|
fields << name
|
181
201
|
end
|
182
202
|
|
@@ -229,16 +249,6 @@ module PacketGen
|
|
229
249
|
undef_method "#{name}=" if method_defined?("#{name}=")
|
230
250
|
end
|
231
251
|
|
232
|
-
# Delete a previously defined field
|
233
|
-
# @param [Symbol] name
|
234
|
-
# @return [void]
|
235
|
-
# @deprecated Use {.remove_field} instead.
|
236
|
-
# @since 2.8.4 deprecated
|
237
|
-
def delete_field(name)
|
238
|
-
Deprecation.deprecated(self, __method__, 'remove_field', klass_method: true)
|
239
|
-
remove_field name
|
240
|
-
end
|
241
|
-
|
242
252
|
# Update a previously defined field
|
243
253
|
# @param [Symbol] field field name to create
|
244
254
|
# @param [Hash] options See {.define_field}.
|
@@ -249,11 +259,11 @@ module PacketGen
|
|
249
259
|
def update_field(field, options)
|
250
260
|
raise ArgumentError, "unkown #{field} field for #{self}" unless @field_defs.key?(field)
|
251
261
|
|
252
|
-
@field_defs[field]
|
253
|
-
@field_defs[field]
|
254
|
-
@field_defs[field]
|
255
|
-
@field_defs[field]
|
256
|
-
@field_defs[field]
|
262
|
+
@field_defs[field].default = options.delete(:default) if options.key?(:default)
|
263
|
+
@field_defs[field].builder = options.delete(:builder) if options.key?(:builder)
|
264
|
+
@field_defs[field].optional = options.delete(:optional) if options.key?(:optional)
|
265
|
+
@field_defs[field].enum = options.delete(:enum) if options.key?(:enum)
|
266
|
+
@field_defs[field].options.merge!(options)
|
257
267
|
end
|
258
268
|
|
259
269
|
# Define a bitfield on given attribute
|
@@ -279,7 +289,7 @@ module PacketGen
|
|
279
289
|
attr_def = @field_defs[attr]
|
280
290
|
raise ArgumentError, "unknown #{attr} field" if attr_def.nil?
|
281
291
|
|
282
|
-
type = attr_def.
|
292
|
+
type = attr_def.type
|
283
293
|
unless type < Types::Int
|
284
294
|
raise TypeError, "#{attr} is not a PacketGen::Types::Int"
|
285
295
|
end
|
@@ -344,12 +354,12 @@ module PacketGen
|
|
344
354
|
|
345
355
|
fields.each do |field, size|
|
346
356
|
undef_method "#{field}="
|
347
|
-
undef_method(size == 1 ? "#{field}?" :
|
357
|
+
undef_method(size == 1 ? "#{field}?" : field)
|
348
358
|
end
|
349
359
|
end
|
350
360
|
end
|
351
361
|
|
352
|
-
# Create a new
|
362
|
+
# Create a new fields object
|
353
363
|
# @param [Hash] options Keys are symbols. They should have name of object
|
354
364
|
# attributes, as defined by {.define_field} and by {.define_bit_fields_on}.
|
355
365
|
def initialize(options={})
|
@@ -358,9 +368,14 @@ module PacketGen
|
|
358
368
|
|
359
369
|
field_defs = self.class.class_eval { @field_defs }
|
360
370
|
self.class.fields.each do |field|
|
361
|
-
|
362
|
-
|
371
|
+
type = field_defs[field].type
|
372
|
+
default = field_defs[field].default
|
363
373
|
default = default.to_proc.call(self) if default.is_a?(Proc)
|
374
|
+
builder = field_defs[field].builder
|
375
|
+
optional = field_defs[field].optional
|
376
|
+
enum = field_defs[field].enum
|
377
|
+
field_options = field_defs[field].options
|
378
|
+
|
364
379
|
@fields[field] = if builder
|
365
380
|
builder.call(self, type)
|
366
381
|
elsif enum
|
@@ -374,17 +389,6 @@ module PacketGen
|
|
374
389
|
value = options[field] || default
|
375
390
|
if value.class <= type
|
376
391
|
@fields[field] = value
|
377
|
-
elsif type < Types::Enum
|
378
|
-
case value
|
379
|
-
when ::String
|
380
|
-
@fields[field].value = value
|
381
|
-
else
|
382
|
-
@fields[field].read(value)
|
383
|
-
end
|
384
|
-
elsif type < Types::Int
|
385
|
-
@fields[field].read(value)
|
386
|
-
elsif type <= Types::String
|
387
|
-
@fields[field].read(value)
|
388
392
|
elsif @fields[field].respond_to? :from_human
|
389
393
|
@fields[field].from_human(value)
|
390
394
|
end
|
@@ -430,12 +434,6 @@ module PacketGen
|
|
430
434
|
@optional_fields.key? field
|
431
435
|
end
|
432
436
|
|
433
|
-
# @deprecated Use {#optional?} instead.
|
434
|
-
def is_optional?(field)
|
435
|
-
Deprecation.deprecated(self.class, __method__, 'optional?', klass_method: true)
|
436
|
-
optional? field
|
437
|
-
end
|
438
|
-
|
439
437
|
# Say if an optional field is present
|
440
438
|
# @return [Boolean]
|
441
439
|
def present?(field)
|
@@ -444,14 +442,6 @@ module PacketGen
|
|
444
442
|
@optional_fields[field].call(self)
|
445
443
|
end
|
446
444
|
|
447
|
-
# Say if an optional field is present
|
448
|
-
# @return [Boolean]
|
449
|
-
# @deprecated Use {#present?} instead.
|
450
|
-
def is_present?(field)
|
451
|
-
Deprecation.deprecated(self.class, __method__, 'present?', klass_method: true)
|
452
|
-
present? field
|
453
|
-
end
|
454
|
-
|
455
445
|
# Populate object from a binary string
|
456
446
|
# @param [String] str
|
457
447
|
# @return [Fields] self
|
@@ -463,17 +453,10 @@ module PacketGen
|
|
463
453
|
fields.each do |field|
|
464
454
|
next unless present?(field)
|
465
455
|
|
466
|
-
obj =
|
467
|
-
if self[field].respond_to? :
|
468
|
-
|
469
|
-
obj = self[field].read str[start, width]
|
470
|
-
start += width
|
471
|
-
elsif self[field].respond_to? :sz
|
472
|
-
obj = self[field].read str[start..-1]
|
473
|
-
size = self[field].sz
|
474
|
-
start += size
|
456
|
+
obj = self[field].read str[start..-1]
|
457
|
+
if self[field].respond_to? :sz
|
458
|
+
start += self[field].sz
|
475
459
|
else
|
476
|
-
obj = self[field].read str[start..-1]
|
477
460
|
start = str.size
|
478
461
|
end
|
479
462
|
self[field] = obj unless obj == self[field]
|
@@ -496,7 +479,7 @@ module PacketGen
|
|
496
479
|
next if attr == :body
|
497
480
|
next unless present?(attr)
|
498
481
|
|
499
|
-
result = yield(attr)if block_given?
|
482
|
+
result = yield(attr) if block_given?
|
500
483
|
str << (result || Inspect.inspect_attribute(attr, self[attr], 1))
|
501
484
|
end
|
502
485
|
str
|
@@ -521,36 +504,6 @@ module PacketGen
|
|
521
504
|
Hash[fields.map { |f| [f, @fields[f].to_human] }]
|
522
505
|
end
|
523
506
|
|
524
|
-
# Used to set body as value of body object.
|
525
|
-
# @param [String,Int,Fields,nil] value
|
526
|
-
# @return [void]
|
527
|
-
# @raise [BodyError] no body on given object
|
528
|
-
# @raise [ArgumentError] cannot cram +body+ in +:body+ field
|
529
|
-
# @deprecated
|
530
|
-
def body=(value)
|
531
|
-
Deprecation.deprecated(self.class, __method__)
|
532
|
-
raise BodyError, 'no body field' unless @fields.key? :body
|
533
|
-
|
534
|
-
case body
|
535
|
-
when ::String
|
536
|
-
self[:body].read value
|
537
|
-
when Int, Fields
|
538
|
-
self[:body] = value
|
539
|
-
when NilClass
|
540
|
-
self[:body] = Types::String.new.read('')
|
541
|
-
else
|
542
|
-
raise ArgumentError, "Can't cram a #{body.class} in a :body field"
|
543
|
-
end
|
544
|
-
end
|
545
|
-
|
546
|
-
# Force str to binary encoding
|
547
|
-
# @param [String] str
|
548
|
-
# @return [String]
|
549
|
-
# @deprecated Will be a private method
|
550
|
-
def force_binary(str)
|
551
|
-
PacketGen.force_binary(str)
|
552
|
-
end
|
553
|
-
|
554
507
|
# Get offset of given field in {Fields} structure.
|
555
508
|
# @param [Symbol] field
|
556
509
|
# @return [Integer]
|
@@ -581,9 +534,16 @@ module PacketGen
|
|
581
534
|
# @return [void]
|
582
535
|
def initialize_copy(_other)
|
583
536
|
fields = {}
|
584
|
-
@fields.each { |k,v| fields[k] = v.dup }
|
537
|
+
@fields.each { |k, v| fields[k] = v.dup }
|
585
538
|
@fields = fields
|
586
539
|
end
|
540
|
+
|
541
|
+
# Force str to binary encoding
|
542
|
+
# @param [String] str
|
543
|
+
# @return [String]
|
544
|
+
def force_binary(str)
|
545
|
+
PacketGen.force_binary(str)
|
546
|
+
end
|
587
547
|
end
|
588
548
|
end
|
589
549
|
end
|