ronin-support 0.4.1 → 0.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/ChangeLog.md +75 -32
  2. data/Gemfile +17 -18
  3. data/README.md +9 -10
  4. data/Rakefile +10 -2
  5. data/gemspec.yml +1 -1
  6. data/lib/ronin/binary.rb +21 -0
  7. data/lib/ronin/binary/hexdump.rb +20 -0
  8. data/lib/ronin/binary/hexdump/parser.rb +411 -0
  9. data/lib/ronin/binary/struct.rb +579 -0
  10. data/lib/ronin/binary/template.rb +437 -0
  11. data/lib/ronin/extensions/ip_addr.rb +17 -13
  12. data/lib/ronin/extensions/regexp.rb +45 -0
  13. data/lib/ronin/extensions/string.rb +3 -3
  14. data/lib/ronin/formatting/extensions/binary.rb +1 -0
  15. data/lib/ronin/formatting/extensions/binary/array.rb +63 -0
  16. data/lib/ronin/formatting/extensions/binary/base64.rb +106 -0
  17. data/lib/ronin/formatting/extensions/binary/file.rb +39 -6
  18. data/lib/ronin/formatting/extensions/binary/float.rb +65 -0
  19. data/lib/ronin/formatting/extensions/binary/integer.rb +56 -43
  20. data/lib/ronin/formatting/extensions/binary/string.rb +75 -187
  21. data/lib/ronin/formatting/extensions/text/string.rb +61 -0
  22. data/lib/ronin/fuzzing/extensions/string.rb +21 -8
  23. data/lib/ronin/fuzzing/fuzzing.rb +19 -17
  24. data/lib/ronin/network.rb +2 -1
  25. data/lib/ronin/network/dns.rb +57 -15
  26. data/lib/ronin/network/extensions.rb +0 -1
  27. data/lib/ronin/network/ftp.rb +145 -0
  28. data/lib/ronin/network/http/http.rb +13 -14
  29. data/lib/ronin/network/imap.rb +11 -10
  30. data/lib/ronin/network/mixins.rb +1 -0
  31. data/lib/ronin/network/mixins/ftp.rb +155 -0
  32. data/lib/ronin/network/mixins/ssl.rb +1 -1
  33. data/lib/ronin/network/mixins/tcp.rb +39 -6
  34. data/lib/ronin/network/mixins/udp.rb +121 -1
  35. data/lib/ronin/network/mixins/unix.rb +279 -0
  36. data/lib/ronin/network/pop3.rb +5 -5
  37. data/lib/ronin/network/proxy.rb +578 -0
  38. data/lib/ronin/network/smtp/email.rb +1 -1
  39. data/lib/ronin/network/smtp/smtp.rb +7 -8
  40. data/lib/ronin/network/ssl.rb +1 -6
  41. data/lib/ronin/network/tcp.rb +2 -305
  42. data/lib/ronin/network/tcp/proxy.rb +377 -0
  43. data/lib/ronin/network/tcp/tcp.rb +435 -0
  44. data/lib/ronin/network/telnet.rb +27 -23
  45. data/lib/ronin/network/udp.rb +2 -266
  46. data/lib/ronin/network/udp/proxy.rb +169 -0
  47. data/lib/ronin/network/udp/udp.rb +442 -0
  48. data/lib/ronin/network/unix.rb +287 -0
  49. data/lib/ronin/path.rb +2 -2
  50. data/lib/ronin/spec/ui/output.rb +1 -7
  51. data/lib/ronin/support.rb +1 -0
  52. data/lib/ronin/support/inflector.rb +3 -7
  53. data/lib/ronin/support/support.rb +2 -1
  54. data/lib/ronin/support/version.rb +1 -1
  55. data/lib/ronin/ui/output/helpers.rb +13 -15
  56. data/lib/ronin/ui/output/output.rb +2 -2
  57. data/lib/ronin/ui/output/terminal/color.rb +10 -4
  58. data/lib/ronin/wordlist.rb +92 -17
  59. data/ronin-support.gemspec +38 -109
  60. data/spec/binary/hexdump/helpers/hexdumps.rb +13 -0
  61. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/ascii.bin +0 -0
  62. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/hexdump_decimal_shorts.txt +0 -0
  63. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/hexdump_hex_bytes.txt +0 -0
  64. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/hexdump_hex_shorts.txt +0 -0
  65. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/hexdump_octal_bytes.txt +0 -0
  66. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/hexdump_octal_shorts.txt +0 -0
  67. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/hexdump_repeated.txt +0 -0
  68. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_decimal_bytes.txt +0 -0
  69. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_decimal_ints.txt +0 -0
  70. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_decimal_quads.txt +0 -0
  71. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_decimal_shorts.txt +0 -0
  72. data/spec/binary/hexdump/helpers/hexdumps/od_doubles.txt +17 -0
  73. data/spec/binary/hexdump/helpers/hexdumps/od_floats.txt +17 -0
  74. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_hex_bytes.txt +0 -0
  75. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_hex_ints.txt +0 -0
  76. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_hex_quads.txt +0 -0
  77. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_hex_shorts.txt +0 -0
  78. data/spec/binary/hexdump/helpers/hexdumps/od_named_chars.txt +17 -0
  79. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_octal_bytes.txt +0 -0
  80. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_octal_ints.txt +0 -0
  81. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_octal_quads.txt +0 -0
  82. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_octal_shorts.txt +0 -0
  83. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/od_repeated.txt +0 -0
  84. data/spec/{formatting/binary → binary/hexdump}/helpers/hexdumps/repeated.bin +0 -0
  85. data/spec/binary/hexdump/parser_spec.rb +302 -0
  86. data/spec/binary/struct_spec.rb +496 -0
  87. data/spec/binary/template_spec.rb +400 -0
  88. data/spec/extensions/ip_addr_spec.rb +58 -32
  89. data/spec/extensions/regexp_spec.rb +60 -0
  90. data/spec/extensions/string_spec.rb +1 -1
  91. data/spec/formatting/binary/array_spec.rb +22 -0
  92. data/spec/formatting/binary/base64_spec.rb +50 -0
  93. data/spec/formatting/binary/float_spec.rb +30 -0
  94. data/spec/formatting/binary/integer_spec.rb +54 -40
  95. data/spec/formatting/binary/string_spec.rb +69 -182
  96. data/spec/formatting/text/string_spec.rb +30 -0
  97. data/spec/network/dns_spec.rb +64 -0
  98. data/spec/network/ftp_spec.rb +65 -0
  99. data/spec/network/proxy_spec.rb +121 -0
  100. data/spec/network/shared/unix_server.rb +31 -0
  101. data/spec/network/tcp/proxy_spec.rb +116 -0
  102. data/spec/network/{tcp_spec.rb → tcp/tcp_spec.rb} +24 -1
  103. data/spec/network/telnet_spec.rb +67 -0
  104. data/spec/network/{udp_spec.rb → udp/udp_spec.rb} +24 -1
  105. data/spec/network/unix_spec.rb +183 -0
  106. data/spec/wordlist_spec.rb +74 -13
  107. metadata +129 -85
  108. data/spec/formatting/binary/helpers/hexdumps.rb +0 -16
@@ -0,0 +1,579 @@
1
+ #
2
+ # Copyright (c) 2006-2012 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/binary/template'
21
+
22
+ require 'set'
23
+
24
+ module Ronin
25
+ module Binary
26
+ #
27
+ # Generic Binary Struct class.
28
+ #
29
+ # class Packet < Binary::Struct
30
+ #
31
+ # endian :network
32
+ #
33
+ # layout :length, :uint32,
34
+ # :data, [:uchar, 48]
35
+ #
36
+ # end
37
+ #
38
+ # pkt = Packet.new
39
+ # pkt.length = 5
40
+ # pkt.data = 'hello'
41
+ #
42
+ # buffer = pkt.pack
43
+ # # => "\x00\x00\x00\x05hello\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
44
+ #
45
+ # new_pkt = Packet.unpack(buffer)
46
+ # # => #<Packet: length: 5, data: "hello">
47
+ #
48
+ # @api public
49
+ #
50
+ class Struct
51
+
52
+ #
53
+ # Initializes the structure.
54
+ #
55
+ def initialize
56
+ # initialize the fields in order
57
+ self.class.layout.each do |name|
58
+ self[name] = self.class.default(self.class.fields[name])
59
+ end
60
+ end
61
+
62
+ #
63
+ # The fields in the structure.
64
+ #
65
+ # @return [Hash{Symbol => type, (type, length)}]
66
+ # The field names and types.
67
+ #
68
+ # @api private
69
+ #
70
+ def self.fields
71
+ @fields ||= {}
72
+ end
73
+
74
+ #
75
+ # Determines if a field exists in the structure.
76
+ #
77
+ # @param [Symbol] name
78
+ # The field name.
79
+ #
80
+ # @return [Boolean]
81
+ # Specifies that the field exists in the structure.
82
+ #
83
+ def self.field?(name)
84
+ fields.has_key?(name.to_sym)
85
+ end
86
+
87
+ #
88
+ # Unpacks data into the structure.
89
+ #
90
+ # @param [String] data
91
+ # The data to unpack.
92
+ #
93
+ # @param [Hash] options
94
+ # Unpacking options.
95
+ #
96
+ # @option options [:little, :big, :network] :endian
97
+ # The endianness to apply to types.
98
+ #
99
+ # @return [Struct]
100
+ # The newly unpacked structure.
101
+ #
102
+ def self.unpack(data,options={})
103
+ new().unpack(data,options)
104
+ end
105
+
106
+ #
107
+ # Determines if a field exists in the structure.
108
+ #
109
+ # @param [Symbol] name
110
+ # The name of the field.
111
+ #
112
+ # @return [Boolean]
113
+ # Specifies whether the field exists.
114
+ #
115
+ def field?(name)
116
+ self.class.field?(name)
117
+ end
118
+
119
+ #
120
+ # Reads a value from the structure.
121
+ #
122
+ # @param [Symbol] name
123
+ # The field name.
124
+ #
125
+ # @return [Integer, Float, String, Struct]
126
+ # The value of the field.
127
+ #
128
+ # @raise [ArgumentError]
129
+ # The structure does not contain the field.
130
+ #
131
+ def [](name)
132
+ if field?(name)
133
+ send(name)
134
+ else
135
+ raise(ArgumentError,"no such field '#{name}'")
136
+ end
137
+ end
138
+
139
+ #
140
+ # Writes a value to the structure.
141
+ #
142
+ # @param [Symbol] name
143
+ # The field name.
144
+ #
145
+ # @param [Integer, Float, String, Struct] value
146
+ # The value to write.
147
+ #
148
+ # @return [Integer, Float, String, Struct]
149
+ # The value of the field.
150
+ #
151
+ # @raise [ArgumentError]
152
+ # The structure does not contain the field.
153
+ #
154
+ def []=(name,value)
155
+ if field?(name)
156
+ send("#{name}=",value)
157
+ else
158
+ raise(ArgumentError,"no such field '#{name}'")
159
+ end
160
+ end
161
+
162
+ #
163
+ # The values within the structure.
164
+ #
165
+ # @return [Array<Integer, Float, String, Struct>]
166
+ # The values of the fields.
167
+ #
168
+ def values
169
+ normalize = lambda { |value|
170
+ case value
171
+ when Struct
172
+ value.values
173
+ else
174
+ value
175
+ end
176
+ }
177
+
178
+ self.class.layout.map do |name|
179
+ case (value = self[name])
180
+ when Array
181
+ value.map(&normalize)
182
+ else
183
+ normalize[value]
184
+ end
185
+ end
186
+ end
187
+
188
+ #
189
+ # Clears the fields of the structure.
190
+ #
191
+ # @return [Struct]
192
+ # The cleared structure.
193
+ #
194
+ def clear
195
+ each_field do |struct,name,field|
196
+ struct[name] = self.class.default(field)
197
+ end
198
+
199
+ return self
200
+ end
201
+
202
+ #
203
+ # Packs the structure.
204
+ #
205
+ # @param [Hash] options
206
+ # Pack options.
207
+ #
208
+ # @option options [:little, :big, :network] :endian
209
+ # The endianness to apply to types.
210
+ #
211
+ # @return [String]
212
+ # The packed structure.
213
+ #
214
+ def pack(options={})
215
+ self.class.templates[options].pack(*values.flatten)
216
+ end
217
+
218
+ #
219
+ # Unpacks data into the structure.
220
+ #
221
+ # @param [String] data
222
+ # The data to unpack.
223
+ #
224
+ # @param [Hash] options
225
+ # Unpack options.
226
+ #
227
+ # @option options [:little, :big, :network] :endian
228
+ # The endianness to apply to types.
229
+ #
230
+ # @return [Struct]
231
+ # The unpacked structure.
232
+ #
233
+ def unpack(data,options={})
234
+ values = self.class.templates[options].unpack(data)
235
+
236
+ each_field do |struct,name,(type,length)|
237
+ struct[name] = if length
238
+ if Template::STRING_TYPES.include?(type)
239
+ # string types are combined into a single String
240
+ values.shift
241
+ else
242
+ # shift off an Array of elements
243
+ values.shift(length)
244
+ end
245
+ else
246
+ values.shift
247
+ end
248
+ end
249
+
250
+ return self
251
+ end
252
+
253
+ #
254
+ # @see #pack
255
+ #
256
+ def to_s
257
+ pack
258
+ end
259
+
260
+ #
261
+ # @see #pack
262
+ #
263
+ def to_str
264
+ pack
265
+ end
266
+
267
+ #
268
+ # Inspects the structure.
269
+ #
270
+ # @return [String]
271
+ # The inspected structure.
272
+ #
273
+ def inspect
274
+ "#<#{self.class}: " << self.class.layout.map { |name|
275
+ "#{name}: " << self[name].inspect
276
+ }.join(', ') << '>'
277
+ end
278
+
279
+ protected
280
+
281
+ #
282
+ # Typedefs.
283
+ #
284
+ # @return [Hash{Symbol => Symbol}]
285
+ # The typedef aliases.
286
+ #
287
+ # @api private
288
+ #
289
+ def self.typedefs
290
+ @@typedefs ||= {}
291
+ end
292
+
293
+ #
294
+ # Defines a typedef.
295
+ #
296
+ # @param [Symbol] type
297
+ # The original type.
298
+ #
299
+ # @param [Symbol] type_alias
300
+ # The new type.
301
+ #
302
+ def self.typedef(type,type_alias)
303
+ type = typedefs.fetch(type,type)
304
+
305
+ unless (type.kind_of?(Symbol) || type < Struct)
306
+ raise(TypeError,"#{type.inspect} is not a Symbol or #{Struct}")
307
+ end
308
+
309
+ typedefs[type_alias] = typedefs.fetch(type,type)
310
+ end
311
+
312
+ # core typedefs
313
+ typedef :ulong, :pointer
314
+ typedef :uchar, :bool
315
+
316
+ # *_t typedefs
317
+ typedef :uint8, :uint8_t
318
+ typedef :uint16, :uint16_t
319
+ typedef :uint32, :uint32_t
320
+ typedef :uint64, :uint64_t
321
+ typedef :int8, :int8_t
322
+ typedef :int16, :int16_t
323
+ typedef :int32, :int32_t
324
+ typedef :int64, :int64_t
325
+
326
+ # network endian types
327
+ typedef :uint16_be, :uint16_net
328
+ typedef :uint32_be, :uint32_net
329
+ typedef :uint64_be, :uint64_net
330
+ typedef :int16_be, :int16_net
331
+ typedef :int32_be, :int32_net
332
+ typedef :int64_be, :int64_net
333
+ typedef :ushort_be, :ushort_net
334
+ typedef :uint_be, :uint_net
335
+ typedef :ulong_be, :ulong_net
336
+ typedef :ulong_long_be, :ulong_long_net
337
+ typedef :int_be, :int_net
338
+ typedef :long_be, :long_net
339
+ typedef :long_long_be, :long_long_net
340
+
341
+ # libc typedefs
342
+ typedef :long, :blkcnt_t
343
+ typedef :pointer, :caddr_t
344
+ typedef :int, :clockid_t
345
+ typedef :int, :daddr_t
346
+ typedef :ulong, :dev_t
347
+ typedef :long, :fd_mask
348
+ typedef :ulong, :fsblkcnt_t
349
+ typedef :ulong, :fsfilcnt_t
350
+ typedef :uint32, :git_t
351
+ typedef :uint32, :id_t
352
+ typedef :ulong, :ino_t
353
+ typedef :int32, :key_t
354
+ typedef :long, :loff_t
355
+ typedef :uint32, :mode_t
356
+ typedef :ulong, :nlink_t
357
+ typedef :long, :off_t
358
+ typedef :int32, :pid_t
359
+ typedef :uint32, :pthread_key_t
360
+ typedef :int32, :pthread_once_t
361
+ typedef :ulong, :pthread_t
362
+ typedef :long, :quad_t
363
+ typedef :long, :register_t
364
+ typedef :ulong, :rlim_t
365
+ typedef :uint16, :sa_family_t
366
+ typedef :ulong, :size_t
367
+ typedef :uint32, :socklen_t
368
+ typedef :long, :suseconds_t
369
+ typedef :long, :ssize_t
370
+ typedef :long, :time_t
371
+ typedef :pointer, :timer_t
372
+ typedef :uint32, :uid_t
373
+
374
+ #
375
+ # Sets or gets the endianness of the structure.
376
+ #
377
+ # @param [:little, :big, :network, nil] type
378
+ # The new endianness.
379
+ #
380
+ # @return [:little, :big, :network, nil]
381
+ # The endianness of the structure.
382
+ #
383
+ def self.endian(type=nil)
384
+ if type
385
+ @endian = type.to_sym
386
+ else
387
+ @endian
388
+ end
389
+ end
390
+
391
+ #
392
+ # The layout of the structure.
393
+ #
394
+ # @param [Array<(name, type)>] fields
395
+ # The new fields for the structure.
396
+ #
397
+ # @return [Array<Symbol>]
398
+ # The field names in order.
399
+ #
400
+ # @example
401
+ # layout :length, :uint32,
402
+ # :data, [:uchar, 256]
403
+ #
404
+ def self.layout(*fields)
405
+ unless fields.empty?
406
+ @layout = []
407
+ @fields = {}
408
+
409
+ fields.each_slice(2) do |name,(type,length)|
410
+ type = typedefs.fetch(type,type)
411
+
412
+ unless (type.kind_of?(Symbol) || type < Struct)
413
+ raise(TypeError,"#{type.inspect} is not a Symbol or #{Struct}")
414
+ end
415
+
416
+ @layout << name
417
+ @fields[name] = [type, length]
418
+
419
+ attr_accessor name
420
+ end
421
+ end
422
+
423
+ return (@layout ||= [])
424
+ end
425
+
426
+ #
427
+ # The templates for the structure.
428
+ #
429
+ # @return [Hash{Hash => Template}]
430
+ # The templates and their options.
431
+ #
432
+ # @api semipublic
433
+ #
434
+ def self.templates
435
+ @templates ||= Hash.new do |hash,options|
436
+ fields = each_field.map { |struct,name,field| field }
437
+ options = {:endian => self.endian}.merge(options)
438
+
439
+ hash[options] = template(fields,options)
440
+ end
441
+ end
442
+
443
+ #
444
+ # Creates a new template for the structure.
445
+ #
446
+ # @param [Array<type, (type, length)>] fields
447
+ # The fields of the structure.
448
+ #
449
+ # @param [Hash] options
450
+ # Template options.
451
+ #
452
+ # @return [Template]
453
+ # The new template.
454
+ #
455
+ # @api semipublic
456
+ #
457
+ def self.template(fields,options={})
458
+ Template.new(fields,options)
459
+ end
460
+
461
+ #
462
+ # Default value for a field.
463
+ #
464
+ # @param [type, (type, length)] type
465
+ # The type of the field.
466
+ #
467
+ # @return [Integer, Float, String, Struct]
468
+ # The default value for the type.
469
+ #
470
+ # @api private
471
+ #
472
+ def self.default(type)
473
+ type, length = type
474
+
475
+ if length
476
+ if Template::STRING_TYPES.include?(type)
477
+ # arrays of chars should be Strings
478
+ String.new
479
+ else
480
+ # create an array of values
481
+ Array.new(length) { |index| default(type) }
482
+ end
483
+ else
484
+ if type.kind_of?(Symbol)
485
+ if Template::INT_TYPES.include?(type)
486
+ 0
487
+ elsif Template::FLOAT_TYPES.include?(type)
488
+ 0.0
489
+ elsif Template::CHAR_TYPES.include?(type)
490
+ "\0"
491
+ elsif Template::STRING_TYPES.include?(type)
492
+ ''
493
+ end
494
+ elsif type < Struct
495
+ type.new
496
+ end
497
+ end
498
+ end
499
+
500
+ #
501
+ # Enumerates the fields of the structure, and all nested structures.
502
+ #
503
+ # @yield [struct, name, type]
504
+ # The given block will be passed each structure, field name and field
505
+ # type.
506
+ #
507
+ # @yieldparam [Struct] struct
508
+ # The structure class.
509
+ #
510
+ # @yieldparam [Symbol] name
511
+ # The name of the field.
512
+ #
513
+ # @yieldparam [type, (type, length)] type
514
+ # The type of the field.
515
+ #
516
+ # @return [Enumerator]
517
+ # If no block is given, an Enumerator will be returned.
518
+ #
519
+ # @api private
520
+ #
521
+ def self.each_field(&block)
522
+ return enum_for(__method__) unless block
523
+
524
+ layout.each do |name|
525
+ type, length = field = fields[name]
526
+
527
+ if type.kind_of?(Symbol)
528
+ yield self, name, field
529
+ elsif type < Struct
530
+ if length
531
+ length.times { type.each_field(&block) }
532
+ else
533
+ type.each_field(&block)
534
+ end
535
+ end
536
+ end
537
+ end
538
+
539
+ #
540
+ # Enumerates the fields of the structure, and all nested structure.
541
+ #
542
+ # @yield [struct, name, type]
543
+ # The given block will be passed each structure, field name and type.
544
+ #
545
+ # @yieldparam [Struct] struct
546
+ # The structure instance.
547
+ #
548
+ # @yieldparam [Symbol] name
549
+ # The name of the field.
550
+ #
551
+ # @yieldparam [type, (type, length)] type
552
+ # The type of the field.
553
+ #
554
+ # @return [Enumerator]
555
+ # If no block is given, an Enumerator will be returned.
556
+ #
557
+ # @api private
558
+ #
559
+ def each_field(&block)
560
+ return enum_for(__method__) unless block
561
+
562
+ self.class.layout.each do |name|
563
+ type, length = field = self.class.fields[name]
564
+
565
+ if type.kind_of?(Symbol)
566
+ yield self, name, field
567
+ elsif type < Struct
568
+ if length
569
+ self[name].each { |struct| struct.each_field(&block) }
570
+ else
571
+ self[name].each_field(&block)
572
+ end
573
+ end
574
+ end
575
+ end
576
+
577
+ end
578
+ end
579
+ end