packetgen 2.1.3 → 2.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e306dbee54d246dd2044db6610ba27e3fb539215
4
- data.tar.gz: 1103a3a6429fb2b4c8391750cdefa8c7d87ac323
3
+ metadata.gz: 36c2e8a9384d7c08723de6f529893d0dd42e5d8d
4
+ data.tar.gz: 0e91b3917d405a1a09b75a7863685645b52827d9
5
5
  SHA512:
6
- metadata.gz: 592a69c18ea009c84374aa79125134ee136f0ed9050aefb8610ff484447f9cddc66685fcc3449733d2f6a1160de403d51a4465689029090815db45cdf292e12f
7
- data.tar.gz: cf57d3f41ea4eaa30964086ec2a1127a0f2aae14a291b2fc7476819484aaa0a14a5c0681bda1f68ad7ba4aa84edc6196cb7684054ea94c22c91a5626eb2ac533
6
+ metadata.gz: 55414180e5ca3bcc69cee0a00a84cbe79635a2809d3f999d35a9d6fab725b5dfc153ea5cc087c10d08985caf10c60aa7ee23df2fb4537d7ca3d6924789f2e614
7
+ data.tar.gz: 51ea04c7c3a1544911ca26f4c6b8c066bc58aa0255a2dd6241f21b668fee9e16c7def036d62722c7e826f77947128225c1a56d5ee4ef27aa71d0eaff55352b5e
@@ -93,6 +93,7 @@ require_relative 'header/icmpv6'
93
93
  require_relative 'header/gre'
94
94
  require_relative 'header/udp'
95
95
  require_relative 'header/tcp'
96
+ require_relative 'header/eap'
96
97
  require_relative 'header/esp'
97
98
  require_relative 'header/ike'
98
99
  require_relative 'header/dns'
@@ -15,8 +15,8 @@ module PacketGen
15
15
  # @author Sylvain Daubert
16
16
  class ASN1Base < RASN1::Model
17
17
 
18
- # @api private
19
18
  # Reference on packet which owns this header
19
+ # @return [Packet,nil]
20
20
  attr_accessor :packet
21
21
 
22
22
  # Give protocol name for this class
@@ -65,6 +65,25 @@ module PacketGen
65
65
  true
66
66
  end
67
67
 
68
+ # @api private
69
+ # @since 2.1.4
70
+ # Set packet to which this header belongs
71
+ # @param [Packet] packet
72
+ # @return [Packet] packet
73
+ def packet=(packet)
74
+ @packet = packet
75
+ added_to_packet(packet)
76
+ @packet
77
+ end
78
+
79
+ # @abstract This method is called when a header is added to a packet.
80
+ # This base method does nothing but may be overriden by subclasses.
81
+ # @param [Packet] packet packet to which self is added
82
+ # @return [void]
83
+ # @since 2.1.4
84
+ def added_to_packet(packet)
85
+ end
86
+
68
87
  alias :parse :parse!
69
88
  alias :to_s :to_der
70
89
 
@@ -35,7 +35,12 @@ module PacketGen
35
35
  when Proc
36
36
  fields.send "#{self[:key]}=", self[:value].call(nil)
37
37
  else
38
- fields.send "#{self[:key]}=", self[:value]
38
+ attr = if self[:key].to_s.end_with?('?')
39
+ self[:key].to_s[0..-2]
40
+ else
41
+ self[:key]
42
+ end
43
+ fields.send "#{attr}=", self[:value]
39
44
  end
40
45
  end
41
46
  end
@@ -127,9 +132,9 @@ module PacketGen
127
132
  end
128
133
  end
129
134
 
130
- # @api private
131
135
  # Reference on packet which owns this header
132
- attr_accessor :packet
136
+ # @return [Packet,nil]
137
+ attr_reader :packet
133
138
 
134
139
  # On inheritage, create +@known_headers+ class variable
135
140
  # @param [Class] klass
@@ -217,6 +222,25 @@ module PacketGen
217
222
  true
218
223
  end
219
224
 
225
+ # @api private
226
+ # Set packet to which this header belongs
227
+ # @param [Packet] packet
228
+ # @return [Packet] packet
229
+ # @since 2.1.4
230
+ def packet=(packet)
231
+ @packet = packet
232
+ added_to_packet(packet)
233
+ @packet
234
+ end
235
+
236
+ # @abstract This method is called when a header is added to a packet.
237
+ # This base method does nothing but may be overriden by subclasses.
238
+ # @param [Packet] packet packet to which self is added
239
+ # @return [void]
240
+ # @since 2.1.4
241
+ def added_to_packet(packet)
242
+ end
243
+
220
244
  # @api private
221
245
  # Get +header+ id in packet headers array
222
246
  # @param [Header] header
@@ -67,6 +67,7 @@ module PacketGen
67
67
  pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
68
68
  PCAP_TIMEOUT)
69
69
  pcap.inject self.to_s
70
+ pcap.close
70
71
  end
71
72
  end
72
73
  self.add_class PPI
@@ -127,6 +128,7 @@ module PacketGen
127
128
  pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
128
129
  PCAP_TIMEOUT)
129
130
  pcap.inject self.to_s
131
+ pcap.close
130
132
  end
131
133
  end
132
134
  self.add_class RadioTap
@@ -161,11 +163,15 @@ module PacketGen
161
163
  # Control frames may be created this way:
162
164
  # pkt = PacketGen.gen('Dot11::Control', subtype: 13) # Ack control frame
163
165
  # pkt.dot11_control # => PacketGen::Header::Dot11::Control
166
+ # # #dot11 is a shortcut for #dot11_control
167
+ # pkt.dot11 # => PacketGen::Header::Dot11::Control
164
168
  #
165
169
  # === IEEE802.11 management frames
166
170
  # Management frames may be created this way:
167
171
  # pkt = PacketGen.gen('Dot11::Management')
168
172
  # pkt.dot11_management # => PacketGen::Header::Dot11::Management
173
+ # # #dot11 is a shortcut for #dot11_management
174
+ # pkt.dot11 # => PacketGen::Header::Dot11::Management
169
175
  # Management frames are usually specialized, AssociationRequest by example:
170
176
  # pkt.add('Dot11::AssoReq')
171
177
  # pkt.dot11_assoreq # => PacketGen::Header::Dot11::AssoReq
@@ -177,13 +183,14 @@ module PacketGen
177
183
  # Data frames may be created this way:
178
184
  # pkt = PacketGen.gen('Dot11::Data')
179
185
  # pkt.dot11_data # => PacketGen::Header::Dot11::Data
186
+ # # #dot11 is a shortcut for #dot11_data
187
+ # pkt.dot11 # => PacketGen::Header::Dot11::Data
180
188
  #
181
189
  # == Parse Dot11 packets
182
190
  # When parsing a Dot11 packet, Dot11 subclass is created from +type+ value.
183
- # Dot11 header should then be accessed through +Packet#dot11_management+,
184
- # +Packet#dot11_control+ or +Packet#dot11_data+.
185
- #
186
- # A +Packet#dot11+ may exist for unknown types.
191
+ # Dot11 header should then be accessed through +Packet#dot11+, whatever
192
+ # the frame type is. But, specialized methods also exist: by example,
193
+ # for a control frame, +Packet#dot11_control+ may also be used.
187
194
  #
188
195
  # == Send Dot11 packets
189
196
  # To send a Dot11 packet, a RadioTap header is needed:
@@ -366,8 +373,19 @@ module PacketGen
366
373
  def to_w(iface)
367
374
  pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC,
368
375
  PCAP_TIMEOUT)
369
- str = self.to_s
370
- pcap.inject str
376
+ pcap.inject self.to_s
377
+ pcap.close
378
+ end
379
+
380
+ # Callback called when a Dot11 header is added to a packet
381
+ # Here, add +#dot11+ method as a shortcut to existing
382
+ # +#dot11_(control|management|data)+.
383
+ # @param [Packet] packet
384
+ # @return [void]
385
+ def added_to_packet(packet)
386
+ unless packet.respond_to? :dot11
387
+ packet.instance_eval("def dot11(arg=nil); header(#{self.class}, arg); end")
388
+ end
371
389
  end
372
390
 
373
391
  private
@@ -46,11 +46,30 @@ module PacketGen
46
46
  # @return [Types::String,Header::Base]
47
47
  define_field :body, Types::String
48
48
 
49
+ # Populate object from string
50
+ # @param [String] str
51
+ # @return [self]
52
+ def read(str)
53
+ return self if str.nil?
54
+ self[:version].read(str[0, 1])
55
+ self[:type].read(str[1, 1])
56
+ self[:length].read(str[2, 2])
57
+ self[:body].read(str[4, self.length])
58
+ self
59
+ end
60
+
49
61
  # Get human readable type
50
62
  # @return [String]
51
63
  def human_type
52
64
  self[:type].to_human
53
65
  end
66
+
67
+ # Calculate and set body length
68
+ # @return [Integer]
69
+ # @since 2.1.4
70
+ def calc_length
71
+ self.length = body.sz
72
+ end
54
73
  end
55
74
 
56
75
  Eth.bind_header Dot1x, ethertype: Dot1x::ETHERTYPE
@@ -0,0 +1,238 @@
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 Header
8
+
9
+ # Extensible Authentication Protocol (EAP),
10
+ # {https://tools.ietf.org/html/rfc3748 RFC 3748}
11
+ #
12
+ # A EAP header has:
13
+ # * a {#code} field ({Types::Int8Enum}),
14
+ # * a {#id} field ({Types::Int8}),
15
+ # * a {#length} field ({Types::Int16}).
16
+ # Request (code 1) and Response (code 2) packets also have:
17
+ # * a {#type} field (+Types::Int8Enum+).
18
+ # And Expanded Types (type 254) packets also have:
19
+ # * a {#vendor_id} field ({Types::Int24}),
20
+ # * a {#vendor_type} field ({Types::Int32}).
21
+ # Finally, all packets have a {#body} ({Types::String}).
22
+ #
23
+ # == Create EAP headers
24
+ # An EAP header may be created this way:
25
+ # # create a request header with default type (1)
26
+ # eap = EAP.new(code: 1) # => PacketGen::Header::EAP
27
+ # # the same
28
+ # eap = EAP.new(code: 'Request') # => PacketGen::Header::EAP
29
+ # # create a Response header of type Nak
30
+ # nak = EAP.new(code: 'Response', type: 'Nak')
31
+ #
32
+ # === Specialized headers
33
+ # Some EAP has a specialized class:
34
+ # * EAP-MD5,
35
+ # * EAP-TLS,
36
+ # * EAP-TTLS,
37
+ # * EAP-FAST.
38
+ # Creating such a header is fairly simple:
39
+ # # Generate a EAP-TLS Response (type is forced to 13)
40
+ # eap = EAP::TLS.new(code: 2) # => PacketGen::Header::EAP::TLS
41
+ #
42
+ # == Header accessors
43
+ # EAP headers may be accessed through +Packet#eap+ accessor.
44
+ # As EAP has specialized subclasses ({EAP::MD5}, {EAP::TLS}, {EAP::TTLS} and
45
+ # {EAP::FAST}), these headers may be accessed through +#eap_md5+, +#eap_tls+,
46
+ # +#eap_ttls+ and +#eap_fast+, respectively. But +#eap+ is still here as a
47
+ # shortcut.
48
+ #
49
+ # == Parse EAP packets
50
+ # When parsing an EAP packet, EAP subclass may be created from +type+ value.
51
+ #
52
+ # So result of parsing a EAP header may be a {EAP}, {EAP::MD5}, {EAP::TLS},
53
+ # {EAP::TTLS} or {EAP::FAST} instance. But this instance is still accessible
54
+ # through +Packet#eap+.
55
+ # @author Sylvain Daubert
56
+ # @since 2.1.4
57
+ class EAP < Base
58
+
59
+ # EAP known codes
60
+ CODES = {
61
+ 'Request' => 1,
62
+ 'Response' => 2,
63
+ 'Success' => 3,
64
+ 'Failure' => 4
65
+ }
66
+
67
+ # EAP known request/response types
68
+ TYPES = {
69
+ 'Identity' => 1,
70
+ 'Notification' => 2,
71
+ 'Nak' => 3,
72
+ 'MD5-Challenge' => 4,
73
+ 'One Time Password' => 5,
74
+ 'Generic Token Card' => 6,
75
+ 'EAP-TLS' => 13,
76
+ 'EAP-TTLS' => 21,
77
+ 'EAP-FAST' => 43,
78
+ 'Expanded Types' => 254,
79
+ 'Experimental Use' => 255
80
+ }
81
+
82
+ # @!attribute code
83
+ # @return [Integer] 8-bit EAP code
84
+ define_field :code, Types::Int8Enum, enum: CODES
85
+
86
+ # @!attribute id
87
+ # @return [Integer] 8-bit identifier
88
+ define_field :id, Types::Int8
89
+
90
+ # @!attribute length
91
+ # @return [Integer] 16-bit length of EAP packet
92
+ define_field :length, Types::Int16, default: 4
93
+
94
+ # @!attribute type
95
+ # This field is present only for Request or Response packets,
96
+ # with type different from Expanded Types (254).
97
+ # @return [Integer] 8-bit request or response type
98
+ define_field :type, Types::Int8Enum, enum: TYPES,
99
+ optional: ->(eap) { eap.has_type? }
100
+
101
+ # @!attribute vendor_id
102
+ # This field is present only for Request or Response packets,
103
+ # with type equal to +Expanded Types+ (254).
104
+ # @return [Integer] 24-bit vendor ID
105
+ define_field :vendor_id, Types::Int24,
106
+ optional: ->(eap) { eap.has_type? and eap.type == 254 }
107
+
108
+ # @!attribute vendor_type
109
+ # This field is present only for Request or Response packets,
110
+ # with type equal to +Expanded Types+ (254).
111
+ # @return [Integer] 32-bit vendor type
112
+ define_field :vendor_type, Types::Int32,
113
+ optional: ->(eap) { eap.has_type? and eap.type == 254 }
114
+
115
+ # @!attribute body
116
+ # @return [Types::String, Header::Base]
117
+ define_field :body, Types::String
118
+
119
+ # @return [EAP]
120
+ def initialize(options={})
121
+ super
122
+ calc_length if options[:length].nil?
123
+ end
124
+
125
+ # @private
126
+ alias old_read read
127
+
128
+ # Populate object from a binary string
129
+ # @param [String] str
130
+ # @return [Dot11] may return a subclass object if a more specific class
131
+ # may be determined
132
+ def read(str)
133
+ if self.class == EAP
134
+ super str
135
+
136
+ if has_type?
137
+ obj = case self.type
138
+ when 4
139
+ EAP::MD5.new.read(str)
140
+ when 13
141
+ EAP::TLS.new.read(str)
142
+ when 21
143
+ EAP::TTLS.new.read(str)
144
+ when 43
145
+ EAP::FAST.new.read(str)
146
+ else
147
+ self
148
+ end
149
+ return obj
150
+ else
151
+ return self
152
+ end
153
+ else
154
+ super str
155
+ end
156
+ self
157
+ end
158
+
159
+ # Get human readable code
160
+ # @return [String]
161
+ def human_code
162
+ self[:code].to_human
163
+ end
164
+
165
+ # Get human readable type
166
+ # @return [String]
167
+ # @raise [ParseError] not a Request nor a Response packet
168
+ def human_type
169
+ raise ParseError, 'not a Request nor a Response' unless has_type?
170
+ self[:type].to_human
171
+ end
172
+
173
+ # Is packet a request?
174
+ # @return [Boolean]
175
+ def request?
176
+ code == CODES['Request']
177
+ end
178
+
179
+ # Is packet a response?
180
+ # @return [Boolean]
181
+ def response?
182
+ code == CODES['Response']
183
+ end
184
+
185
+ # Is packet a success?
186
+ # @return [Boolean]
187
+ def success?
188
+ code == CODES['Success']
189
+ end
190
+
191
+ # Is packet a failure?
192
+ # @return [Boolean]
193
+ def failure?
194
+ code == CODES['Failure']
195
+ end
196
+
197
+ # Return an array of desired authentication types from a Nak packet
198
+ # @return [Array<Integer>]
199
+ # @raise [ParseError] not a Nak packet
200
+ def desired_auth_type
201
+ if code != 2 and type != 3
202
+ raise ParseError, 'not a Nak response'
203
+ end
204
+ body.to_s.unpack('C*')
205
+ end
206
+
207
+ # Calculate length field from content
208
+ # @return [Integer]
209
+ def calc_length
210
+ self.length = sz
211
+ end
212
+
213
+ # Say is this EAP header has {#type} field
214
+ # @return [Boolean]
215
+ def has_type?
216
+ [1,2].include?(self.code)
217
+ end
218
+
219
+ # Callback called when a EAP header is added to a packet
220
+ # Here, add +#eap+ method as a shortcut to existing
221
+ # +#eap_(md5|tls|ttls|fast)+.
222
+ # @param [Packet] packet
223
+ # @return [void]
224
+ def added_to_packet(packet)
225
+ unless packet.respond_to? :eap
226
+ packet.instance_eval("def eap(arg=nil); header(#{self.class}, arg); end")
227
+ end
228
+ end
229
+ end
230
+
231
+ Dot1x.bind_header EAP, type: 0
232
+ end
233
+ end
234
+
235
+ require_relative 'eap/md5'
236
+ require_relative 'eap/tls'
237
+ require_relative 'eap/ttls'
238
+ require_relative 'eap/fast'
@@ -0,0 +1,27 @@
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 Header
8
+ class EAP
9
+
10
+ # Extensible Authentication Protocol (EAP) - Flexible Authentication variable
11
+ # Secure Tunneling, {https://tools.ietf.org/html/rfc4851 RFC 4851}
12
+ #
13
+ # {EAP::FAST} has following fields:
14
+ # * {#flags} ({Types::Int8}),
15
+ # * optionally {#message_length} ({Types::Int32}), if +#l?+ is +true+,
16
+ # * {#body} ({Types::String}).
17
+ # @author Sylvain Daubert
18
+ # @since 2.1.4
19
+ class FAST < TTLS
20
+ # @return [EAP::FAST]
21
+ def initialize(options={})
22
+ super({ type: 43 }.merge!(options))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
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 Header
8
+ class EAP
9
+
10
+ # Extensible Authentication Protocol (EAP) -
11
+ # {https://tools.ietf.org/html/rfc3748#section-5.4 MD5 challenge}
12
+ # @author Sylvain Daubert
13
+ # @since 2.1.4
14
+ class MD5 < EAP
15
+ delete_field :body
16
+ # @!attribute value_size
17
+ # @return [Integer] 8-bit value size
18
+ define_field :value_size, Types::Int8
19
+ # @!attribute value
20
+ # @return [::String]
21
+ define_field :value, Types::String,
22
+ builder: ->(h) { Types::String.new('', length_from: h[:value_size]) }
23
+ # @!attribute optional_name
24
+ # @return [::String]
25
+ define_field :optional_name, Types::String
26
+
27
+ # @return [EAP::MD5]
28
+ def initialize(options={})
29
+ super({ type: 4 }.merge!(options))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,73 @@
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 Header
8
+ class EAP
9
+
10
+ # Extensible Authentication Protocol (EAP) - TLS,
11
+ # {https://tools.ietf.org/html/rfc5216 RFC 5216}
12
+ #
13
+ # {EAP::TLS} has following fields:
14
+ # * {#flags} ({Types::Int8}),
15
+ # * optionally {#tls_length} ({Types::Int32}), if +#l?+ is +true+,
16
+ # * {#body} ({Types::String}).
17
+ # @author Sylvain Daubert
18
+ # @since 2.1.4
19
+ class TLS < EAP
20
+ # @!attribute flags
21
+ # @return [Integer] 8-bit flags
22
+ define_field_before :body, :flags, Types::Int8
23
+
24
+ # @!attribute l
25
+ # Say if length field is included. Defined on {#flags} field.
26
+ # @return [Boolean]
27
+ # @!attribute m
28
+ # Say if there are more fragments. Defined on {#flags} field.
29
+ # @return [Boolean]
30
+ # @!attribute s
31
+ # If set, this message is a TLS-Start. Defined on {#flags} field.
32
+ # @return [Boolean]
33
+ define_bit_fields_on :flags, :l, :m, :s, :reserved, 5
34
+ alias :length_present? :l?
35
+ alias :more_fragments? :m?
36
+ alias :tls_start? :s?
37
+
38
+ # @!attribute tls_length
39
+ # TLS message length. This field provides the total length of the
40
+ # TLS message or set of messages that is being fragmented. So, it
41
+ # cannot be automatically calculated (no +#calc_length+ method).
42
+ # @return [Integer] 32-bit TLS length
43
+ define_field_before :body, :tls_length, Types::Int32,
44
+ optional: ->(h) { h.l? }
45
+
46
+ # @return [EAP::TLS]
47
+ def initialize(options={})
48
+ super({ type: 13 }.merge!(options))
49
+ end
50
+
51
+ # @return [String]
52
+ def inspect
53
+ str = Inspect.dashed_line(self.class, 2)
54
+ fields.each do |attr|
55
+ next if attr == :body
56
+ next unless is_present?(attr)
57
+ if attr == :flags
58
+ shift = Inspect.shift_level(2)
59
+ value = %i(l m s).map { |f| send("#{f}?") ? f.to_s : '.' }.join
60
+ value = '%-10s (0x%02x)' % [value, self.flags]
61
+ str << shift
62
+ str << Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''),
63
+ attr, value]
64
+ else
65
+ str << Inspect.inspect_attribute(attr, self[attr], 2)
66
+ end
67
+ end
68
+ str
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,78 @@
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 Header
8
+ class EAP
9
+
10
+ # Extensible Authentication Protocol (EAP) - Tunneled-TLS,
11
+ # {https://tools.ietf.org/html/rfc5281 RFC 5281}
12
+ #
13
+ # {EAP::TTLS} has following fields:
14
+ # * {#flags} ({Types::Int8}),
15
+ # * optionally {#message_length} ({Types::Int32}), if +#l?+ is +true+,
16
+ # * {#body} ({Types::String}).
17
+ # @author Sylvain Daubert
18
+ # @since 2.1.4
19
+ class TTLS < EAP
20
+ # @!attribute flags
21
+ # @return [Integer] 8-bit flags
22
+ define_field_before :body, :flags, Types::Int8
23
+
24
+ # @!attribute l
25
+ # Say if length field is included. Defined on {#flags} field.
26
+ # @return [Boolean]
27
+ # @!attribute m
28
+ # Say if there are more fragments. Defined on {#flags} field.
29
+ # @return [Boolean]
30
+ # @!attribute s
31
+ # If set, this message is a TLS-Start. Defined on {#flags} field.
32
+ # @return [Boolean]
33
+ # @!attribute reserved
34
+ # @return [Integer] 2-bit reserved integer
35
+ # @!attribute version
36
+ # @return [Integer] 3-bit version
37
+ define_bit_fields_on :flags, :l, :m, :s, :reserved, 2, :version, 3
38
+ alias :length_present? :l?
39
+ alias :more_fragments? :m?
40
+ alias :tls_start? :s?
41
+
42
+ # @!attribute message_length
43
+ # Message length. This field provides the total length of the
44
+ # raw data message sequence prior to fragmentation. So, it
45
+ # cannot be automatically calculated (no +#calc_length+ method).
46
+ # @return [Integer] 32-bit message length
47
+ define_field_before :body, :message_length, Types::Int32,
48
+ optional: ->(h) { h.l? }
49
+
50
+ # @return [EAP::TTLS]
51
+ def initialize(options={})
52
+ super({ type: 21 }.merge!(options))
53
+ end
54
+
55
+ # @return [String]
56
+ def inspect
57
+ str = Inspect.dashed_line(self.class, 2)
58
+ fields.each do |attr|
59
+ next if attr == :body
60
+ next unless is_present?(attr)
61
+ if attr == :flags
62
+ shift = Inspect.shift_level(2)
63
+ value = %i(l m s).map { |f| send("#{f}?") ? f.to_s : '.' }.join
64
+ value = '%-10s (0x%02x)' % [value, self.flags]
65
+ str << shift
66
+ str << Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''),
67
+ attr, value]
68
+ str << Inspect::FMT_ATTR % ['', 'version', self.version]
69
+ else
70
+ str << Inspect.inspect_attribute(attr, self[attr], 2)
71
+ end
72
+ end
73
+ str
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -96,6 +96,7 @@ module PacketGen
96
96
  def to_w(iface)
97
97
  pcap = PCAPRUB::Pcap.open_live(iface, PCAP_SNAPLEN, PCAP_PROMISC, PCAP_TIMEOUT)
98
98
  pcap.inject self.to_s
99
+ pcap.close
99
100
  end
100
101
  end
101
102
 
@@ -38,27 +38,26 @@ module PacketGen
38
38
  # Payload type number
39
39
  PAYLOAD_TYPE = 39
40
40
 
41
- METHOD_RSA_SIGNATURE = 1
42
- METHOD_SHARED_KEY = 2
43
- METHOD_DSA_SIGNATURE = 3
44
- METHOD_ECDSA256 = 9
45
- METHOD_ECDSA384 = 10
46
- METHOD_ECDSA512 = 11
47
- METHOD_PASSWORD = 12
48
- METHOD_NULL = 13
49
- METHOD_DIGITAL_SIGNATURE = 14
41
+ METHODS = {
42
+ 'RSA_SIGNATURE' => 1,
43
+ 'SHARED_KEY' => 2,
44
+ 'DSA_SIGNATURE' => 3,
45
+ 'ECDSA256' => 9,
46
+ 'ECDSA384' => 10,
47
+ 'ECDSA512' => 11,
48
+ 'PASSWORD' => 12,
49
+ 'NULL' => 13,
50
+ 'DIGITAL_SIGNATURE' => 14
51
+ }
50
52
 
51
- # @attribute :u32
52
- # 32-bit word including ID Type and RESERVED fields
53
- # @return [Integer]
54
- define_field_before :content, :u32, Types::Int32
55
53
  # @attribute [r] method
56
54
  # 8-bit Auth Method
57
55
  # @return [Integer]
56
+ define_field_before :content, :method, Types::Int8Enum, enum: METHODS
58
57
  # @attribute reserved
59
58
  # 24-bit reserved field
60
59
  # @return [Integer]
61
- define_bit_fields_on :u32, :method, 8, :reserved, 24
60
+ define_field_before :content, :reserved, Types::Int24
62
61
 
63
62
  # Check authentication (see RFC 7296 §2.15)
64
63
  # @param [Packet] init_msg first IKE message sent by peer
@@ -83,10 +82,11 @@ module PacketGen
83
82
  signed_octets << prf(prf, sk_p, id.to_s[4, id.length - 4])
84
83
 
85
84
  case method
86
- when METHOD_SHARED_KEY
85
+ when METHODS['SHARED_KEY']
87
86
  auth = prf(prf(shared_secret, 'Key Pad for IKEv2'), signed_octets)
88
87
  auth == content
89
- when METHOD_RSA_SIGNATURE, METHOD_ECDSA256, METHOD_ECDSA384, METHOD_ECDSA512
88
+ when METHODS['RSA_SIGNATURE'], METHODS['ECDSA256'], METHODS['ECDSA384'],
89
+ METHODS['ECDSA512']
90
90
  if packet.ike_cert
91
91
  # FIXME: Expect a ENCODING_X509_CERT_SIG
92
92
  # Others types not supported for now...
@@ -122,46 +122,10 @@ module PacketGen
122
122
  end
123
123
  end
124
124
 
125
- # Set Auth method
126
- # @param [Integer,String] value
127
- # @return [Integer]
128
- def method=(value)
129
- method = case value
130
- when Integer
131
- value
132
- else
133
- c = self.class.constants.grep(/METHOD_#{value}/).first
134
- c ? self.class.const_get(c) : nil
135
- end
136
- raise ArgumentError, "unknown auth method #{value.inspect}" unless method
137
- self[:u32].value = (self[:u32].to_i & 0xffffff) | (method << 24)
138
- end
139
-
140
125
  # Get authentication method name
141
126
  # @return [String]
142
127
  def human_method
143
- name = self.class.constants.grep(/METHOD_/).
144
- select { |c| self.class.const_get(c) == method }.
145
- first || "method #{method}"
146
- name.to_s.sub(/METHOD_/, '')
147
- end
148
-
149
- # @return [String]
150
- def inspect
151
- str = Inspect.dashed_line(self.class, 2)
152
- fields.each do |attr|
153
- case attr
154
- when :body
155
- next
156
- when :u32
157
- str << Inspect.shift_level(2)
158
- str << Inspect::FMT_ATTR % ['Int8', :method, human_method]
159
- str << Inspect.inspect_attribute(:reserved, self.reserved, 2)
160
- else
161
- str << Inspect.inspect_attribute(attr, self[attr], 2)
162
- end
163
- end
164
- str
128
+ self[:method].to_human
165
129
  end
166
130
 
167
131
  private
@@ -40,80 +40,43 @@ module PacketGen
40
40
  # Payload type number
41
41
  PAYLOAD_TYPE = 35
42
42
 
43
- TYPE_IPV4_ADDR = 1
44
- TYPE_FQDN = 2
45
- TYPE_RFC822_ADDR = 3
46
- TYPE_IPV6_ADDR = 5
47
- TYPE_DER_ASN1_DN = 9
48
- TYPE_DER_ASN1_GN = 10
49
- TYPE_KEY_ID = 11
43
+ TYPES = {
44
+ 'IPV4_ADDR' => 1,
45
+ 'FQDN' => 2,
46
+ 'RFC822_ADDR' => 3,
47
+ 'IPV6_ADDR' => 5,
48
+ 'DER_ASN1_DN' => 9,
49
+ 'DER_ASN1_GN' => 10,
50
+ 'KEY_ID' => 11
51
+ }
50
52
 
51
- # @attribute :u32
52
- # 32-bit word including ID Type and RESERVED fields
53
- # @return [Integer]
54
- define_field_before :content, :u32, Types::Int32
55
53
  # @attribute [r] type
56
54
  # 8-bit ID type
57
55
  # @return [Integer]
56
+ define_field_before :content, :type, Types::Int8Enum, enum: TYPES
58
57
  # @attribute reserved
59
58
  # 24-bit reserved field
60
59
  # @return [Integer]
61
- define_bit_fields_on :u32, :type, 8, :reserved, 24
62
-
63
- # Set ID type
64
- # @param [Integer,String] value
65
- # @return [Integer]
66
- def type=(value)
67
- type = case value
68
- when Integer
69
- value
70
- else
71
- c = self.class.constants.grep(/TYPE_#{value}/).first
72
- c ? self.class.const_get(c) : nil
73
- end
74
- raise ArgumentError, "unknown ID type #{value.inspect}" unless type
75
- self[:u32].value = (self[:u32].to_i & 0xffffff) | (type << 24)
76
- end
60
+ define_field_before :content, :reserved, Types::Int24
77
61
 
78
62
  # Get ID type name
79
63
  # @return [String]
80
64
  def human_type
81
- name = self.class.constants.grep(/TYPE_/).
82
- select { |c| self.class.const_get(c) == type }.
83
- first || "type #{type}"
84
- name.to_s.sub(/TYPE_/, '')
65
+ self[:type].to_human
85
66
  end
86
67
 
87
68
  # Get human readable content, from {#type}
88
69
  # @return [String]
89
70
  def human_content
90
71
  case type
91
- when TYPE_IPV4_ADDR, TYPE_IPV4_ADDR
72
+ when TYPES['IPV4_ADDR'], TYPES['IPV4_ADDR']
92
73
  IPAddr.ntop(content)
93
- when TYPE_DER_ASN1_DN, TYPE_DER_ASN1_GN
74
+ when TYPES['DER_ASN1_DN'], TYPES['DER_ASN1_GN']
94
75
  OpenSSL::X509::Name.new(content).to_s
95
76
  else
96
77
  content.inspect
97
78
  end
98
79
  end
99
-
100
- # @return [String]
101
- def inspect
102
- str = Inspect.dashed_line(self.class, 2)
103
- fields.each do |attr|
104
- case attr
105
- when :body
106
- next
107
- when :u32
108
- str << Inspect.shift_level(2)
109
- str << Inspect::FMT_ATTR % ['Int8', :type, human_type]
110
- str << Inspect.inspect_attribute(:reserved, self.reserved, 2)
111
- else
112
- str << Inspect.inspect_attribute(attr, self[attr], 2)
113
- end
114
- end
115
- str
116
- end
117
80
  end
118
81
 
119
82
  # This class handles Identification - Responder payloads, denoted IDr.
@@ -225,14 +225,10 @@ module PacketGen
225
225
  # 8-bit Number of TSs
226
226
  # @return [Integer]
227
227
  define_field_before :body, :num_ts, Types::Int8
228
- # @!attribute rsv1
229
- # First 8-bit RESERVED field
228
+ # @!attribute rsv
229
+ # 24-bit RESERVED field
230
230
  # @return [Integer]
231
- define_field_before :body, :rsv1, Types::Int8
232
- # @!attribute rsv2
233
- # Last 16-bit RESERVED field
234
- # @return [Integer]
235
- define_field_before :body, :rsv2, Types::Int16
231
+ define_field_before :body, :rsv, Types::Int24
236
232
 
237
233
  # @!attribute traffic_selectors
238
234
  # Set of {TrafficSelector}
@@ -259,24 +255,6 @@ module PacketGen
259
255
  selectors.each { |p| p.calc_length }
260
256
  super
261
257
  end
262
-
263
- # @return [String]
264
- def inspect
265
- str = Inspect.dashed_line(self.class, 2)
266
- fields.each do |attr|
267
- case attr
268
- when :body, :rsv2
269
- next
270
- when :rsv1
271
- str << Inspect.shift_level(2)
272
- str << Inspect::FMT_ATTR % ['Int24', 'reserved',
273
- (rsv1 << 16) | rsv2 ]
274
- else
275
- str << Inspect.inspect_attribute(attr, self[attr], 2)
276
- end
277
- end
278
- str
279
- end
280
258
  end
281
259
 
282
260
  class TSr < TSi
@@ -201,6 +201,7 @@ module PacketGen
201
201
  sock = Socket.new(Socket::AF_INET, Socket::SOCK_RAW, Socket::IPPROTO_RAW)
202
202
  sockaddrin = Socket.sockaddr_in(0, dst)
203
203
  sock.send to_s, 0, sockaddrin
204
+ sock.close
204
205
  end
205
206
 
206
207
  # @return [String]
@@ -196,6 +196,7 @@ module PacketGen
196
196
  pkt_info = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip(src), ifaddr.ifindex)
197
197
 
198
198
  sock.sendmsg body.to_s, 0, sockaddrin, hop_limit, tc, pkt_info
199
+ sock.close
199
200
  end
200
201
 
201
202
  # @return [String]
@@ -49,8 +49,10 @@ module PacketGen
49
49
  # @return [String]
50
50
  def self.inspect_attribute(attr, value, level=1)
51
51
  str = shift_level(level)
52
- val = if value.is_a?(Types::Int) or value.is_a?(Integer)
53
- int_dec_hex(value, value.to_s.size * 2)
52
+ val = if value.is_a?(Types::Enum)
53
+ "%-10s (0x%0#{value.sz * 2}x)" % [value.to_human, value.to_i]
54
+ elsif value.is_a?(Types::Int) or value.is_a?(Integer)
55
+ int_dec_hex(value, value.sz * 2)
54
56
  elsif value.respond_to? :to_human
55
57
  value.to_human
56
58
  else
@@ -209,11 +209,23 @@ module PacketGen
209
209
 
210
210
  # send packet on wire. Use first header +#to_w+ method.
211
211
  # @param [String] iface interface name. Default to first non-loopback interface
212
+ # @param [Boolean] calc call {#calc} on packet before sending it
213
+ # @param [Integer] number number of times to send the packets
214
+ # @param [Integer,Float] interval time, in seconds, between sending 2 packets
212
215
  # @return [void]
213
- def to_w(iface=nil)
216
+ # @since 2.1.4 add `calc`, `number` and `interval` parameters
217
+ def to_w(iface=nil, calc: false, number: 1, interval: 1)
214
218
  iface ||= PacketGen.default_iface
215
219
  if @headers.first.respond_to? :to_w
216
- @headers.first.to_w(iface)
220
+ self.calc if calc
221
+ if number == 1
222
+ @headers.first.to_w(iface)
223
+ else
224
+ number.times do
225
+ @headers.first.to_w(iface)
226
+ sleep interval
227
+ end
228
+ end
217
229
  else
218
230
  type = @headers.first.protocol_name
219
231
  raise WireError, "don't known how to send a #{type} packet on wire"
@@ -360,8 +372,8 @@ module PacketGen
360
372
  header.packet = self
361
373
  @headers << header unless previous_header
362
374
  unless respond_to? header.method_name
363
- self.class.class_eval "def #{header.method_name}(arg=nil);" \
364
- "header(#{header.class}, arg); end"
375
+ self.instance_eval "def #{header.method_name}(arg=nil);" \
376
+ "header(#{header.class}, arg); end"
365
377
  end
366
378
  end
367
379
 
@@ -151,9 +151,6 @@ module PacketGen
151
151
  end
152
152
 
153
153
  # Translates a {File} into an array of packets.
154
- # Note that this strips out timestamps -- if you'd like to retain
155
- # timestamps and other pcapng file information, you will want to
156
- # use {#read} instead.
157
154
  # @param [Hash] options
158
155
  # @option options [String] :filename if given, object is cleared and filename
159
156
  # is analyzed before generating array. Else, array is generated from +self+
@@ -293,34 +293,35 @@ module PacketGen
293
293
  @optional_fields = {}
294
294
 
295
295
  self.class.class_eval { @field_defs }.each do |field, ary|
296
- default = ary[1].is_a?(Proc) ? ary[1].call : ary[1]
297
- @fields[field] = if ary[2]
298
- ary[2].call(self)
299
- elsif ary[4]
300
- ary[0].new(ary[4])
301
- elsif !ary[5].empty?
302
- ary[0].new(ary[5])
296
+ type, default, builder, optional, enum, field_options = ary
297
+ default = default.call if default.is_a?(Proc)
298
+ @fields[field] = if builder
299
+ builder.call(self)
300
+ elsif enum
301
+ type.new(enum)
302
+ elsif !field_options.empty?
303
+ type.new(field_options)
303
304
  else
304
- ary[0].new
305
+ type.new
305
306
  end
306
307
 
307
308
  value = options[field] || default
308
- if ary[0] < Types::Enum
309
+ if type < Types::Enum
309
310
  case value
310
311
  when ::String
311
312
  @fields[field].value = value
312
313
  else
313
314
  @fields[field].read(value)
314
315
  end
315
- elsif ary[0] < Types::Int
316
+ elsif type < Types::Int
316
317
  @fields[field].read(value)
317
- elsif ary[0] <= Types::String
318
+ elsif type <= Types::String
318
319
  @fields[field].read(value)
319
320
  else
320
321
  @fields[field].from_human(value) if @fields[field].respond_to? :from_human
321
322
  end
322
323
 
323
- @optional_fields[field] = ary[3] if ary[3]
324
+ @optional_fields[field] = optional if optional
324
325
  end
325
326
  self.class.class_eval { @bit_fields }.each do |bit_field|
326
327
  self.send "#{bit_field}=", options[bit_field] if options[bit_field]
@@ -350,7 +351,7 @@ module PacketGen
350
351
 
351
352
  # Get all optional field name
352
353
  def optional_fields
353
- fields.select { |f| is_optional?(f) }
354
+ @optional_fields.keys
354
355
  end
355
356
 
356
357
  # Say if this field is optional
@@ -117,6 +117,67 @@ module PacketGen
117
117
  end
118
118
  end
119
119
 
120
+ # 3-byte integer
121
+ # @author Sylvain Daubert
122
+ # @since 2.1.4
123
+ class Int24 < Int
124
+ # @param [Integer,nil] value
125
+ # @param [:big, :little] endian
126
+ def initialize(value=nil, endian=:big)
127
+ super(value, endian, 3)
128
+ end
129
+
130
+ # Read an 3-byte Int from a binary string or an integer
131
+ # @param [Integer, String] value
132
+ # @return [self]
133
+ def read(value)
134
+ return self if value.nil?
135
+ @value = if value.is_a?(Integer)
136
+ value.to_i
137
+ else
138
+ up8 = down16 = 0
139
+ if @endian == :big
140
+ up8, down16 = value.to_s.unpack('Cn')
141
+ else
142
+ down16, up8 = value.to_s.unpack('vC')
143
+ end
144
+ (up8 << 16) | down16
145
+ end
146
+ self
147
+ end
148
+
149
+ # @return [::String]
150
+ def to_s
151
+ up8 = to_i >> 16
152
+ down16 = to_i & 0xffff
153
+ if @endian == :little
154
+ [up8, down16].pack('Cn')
155
+ else
156
+ [down16, up8].pack('vC')
157
+ end
158
+ end
159
+ end
160
+
161
+ # big endian 3-byte integer
162
+ # @author Sylvain Daubert
163
+ # @since 2.1.4
164
+ class Int24be < Int24
165
+ undef endian=
166
+ end
167
+
168
+ # little endian 3-byte integer
169
+ # @author Sylvain Daubert
170
+ # @since 2.1.4
171
+ class Int24le < Int24
172
+ # @param [Integer,nil] value
173
+ undef endian=
174
+
175
+ # @param [Integer, nil] value
176
+ def initialize(value=nil)
177
+ super(value, :little)
178
+ end
179
+ end
180
+
120
181
  # 4-byte integer
121
182
  # @author Sylvain Daubert
122
183
  class Int32 < Int
@@ -8,5 +8,5 @@
8
8
  # @author Sylvain Daubert
9
9
  module PacketGen
10
10
  # PacketGen version
11
- VERSION = '2.1.3'
11
+ VERSION = '2.1.4'
12
12
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packetgen
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-01 00:00:00.000000000 Z
11
+ date: 2017-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pcaprub
@@ -167,6 +167,11 @@ files:
167
167
  - lib/packetgen/header/dot11/sub_mngt.rb
168
168
  - lib/packetgen/header/dot1q.rb
169
169
  - lib/packetgen/header/dot1x.rb
170
+ - lib/packetgen/header/eap.rb
171
+ - lib/packetgen/header/eap/fast.rb
172
+ - lib/packetgen/header/eap/md5.rb
173
+ - lib/packetgen/header/eap/tls.rb
174
+ - lib/packetgen/header/eap/ttls.rb
170
175
  - lib/packetgen/header/esp.rb
171
176
  - lib/packetgen/header/eth.rb
172
177
  - lib/packetgen/header/gre.rb