packetgen 2.1.3 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
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