ruckus 0.5.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.
Files changed (59) hide show
  1. data/.document +5 -0
  2. data/.gitignore +22 -0
  3. data/README +0 -0
  4. data/README.markdown +51 -0
  5. data/Rakefile +54 -0
  6. data/VERSION +1 -0
  7. data/lib/ruckus.rb +70 -0
  8. data/lib/ruckus/blob.rb +113 -0
  9. data/lib/ruckus/choice.rb +55 -0
  10. data/lib/ruckus/dfuzz.rb +231 -0
  11. data/lib/ruckus/dictionary.rb +119 -0
  12. data/lib/ruckus/enum.rb +39 -0
  13. data/lib/ruckus/extensions/array.rb +33 -0
  14. data/lib/ruckus/extensions/class.rb +168 -0
  15. data/lib/ruckus/extensions/duplicable.rb +37 -0
  16. data/lib/ruckus/extensions/file.rb +24 -0
  17. data/lib/ruckus/extensions/fixnum.rb +26 -0
  18. data/lib/ruckus/extensions/hash.rb +10 -0
  19. data/lib/ruckus/extensions/integer.rb +26 -0
  20. data/lib/ruckus/extensions/ipaddr.rb +155 -0
  21. data/lib/ruckus/extensions/irb.rb +30 -0
  22. data/lib/ruckus/extensions/math.rb +6 -0
  23. data/lib/ruckus/extensions/module.rb +37 -0
  24. data/lib/ruckus/extensions/numeric.rb +117 -0
  25. data/lib/ruckus/extensions/object.rb +22 -0
  26. data/lib/ruckus/extensions/range.rb +16 -0
  27. data/lib/ruckus/extensions/socket.rb +20 -0
  28. data/lib/ruckus/extensions/string.rb +327 -0
  29. data/lib/ruckus/filter.rb +16 -0
  30. data/lib/ruckus/human_display.rb +79 -0
  31. data/lib/ruckus/ip.rb +38 -0
  32. data/lib/ruckus/mac_addr.rb +31 -0
  33. data/lib/ruckus/mutator.rb +318 -0
  34. data/lib/ruckus/null.rb +24 -0
  35. data/lib/ruckus/number.rb +360 -0
  36. data/lib/ruckus/parsel.rb +363 -0
  37. data/lib/ruckus/selector.rb +92 -0
  38. data/lib/ruckus/str.rb +164 -0
  39. data/lib/ruckus/structure.rb +263 -0
  40. data/lib/ruckus/structure/atcreate.rb +23 -0
  41. data/lib/ruckus/structure/beforebacks.rb +28 -0
  42. data/lib/ruckus/structure/defaults.rb +57 -0
  43. data/lib/ruckus/structure/factory.rb +42 -0
  44. data/lib/ruckus/structure/fixupfields.rb +16 -0
  45. data/lib/ruckus/structure/initializers.rb +14 -0
  46. data/lib/ruckus/structure/relate.rb +34 -0
  47. data/lib/ruckus/structure/replacement.rb +30 -0
  48. data/lib/ruckus/structure/searchmods.rb +33 -0
  49. data/lib/ruckus/structure/structureproxies.rb +28 -0
  50. data/lib/ruckus/structure/validate.rb +12 -0
  51. data/lib/ruckus/structure_shortcuts.rb +109 -0
  52. data/lib/ruckus/time_t.rb +26 -0
  53. data/lib/ruckus/vector.rb +97 -0
  54. data/ruckus.gemspec +104 -0
  55. data/test/test_decides.rb +61 -0
  56. data/test/test_mutator.rb +29 -0
  57. data/test/test_override.rb +23 -0
  58. data/test/test_replace.rb +39 -0
  59. metadata +138 -0
@@ -0,0 +1,164 @@
1
+ # === Pretty much anything that isn't a Number is a Str
2
+
3
+ # class Symbol
4
+ # def clone
5
+ # self # fuck you ruby, what the fuck is wrong with you
6
+ # end
7
+ # end
8
+
9
+ module Ruckus
10
+ # A Ruckus::Str is a bag of bytes, wrapping a Ruby string
11
+ class Str < Parsel
12
+ # Options include:
13
+ # size:: :min = :max = :size
14
+ # min:: string will be padded to this size
15
+ # max:: string will be cut off at this size
16
+ # padding:: (Default: "\x00") --- what to pad with
17
+ # value:: Normally a string
18
+ # unicode:: Convert to UTF-16-LE before rendering
19
+ #
20
+ def initialize(opts={})
21
+ opts[:bounded_by] ||= -1 if opts[:bounded]
22
+
23
+ if opts[:bounded_by]
24
+ opts[:size] = { :offset => opts[:bounded_by], :meth => :value }
25
+ end
26
+
27
+ if opts[:size]
28
+ opts[:min] = opts[:size]
29
+ opts[:max] = opts[:size]
30
+ @in_size = opts[:size]
31
+ end
32
+ opts[:padding] ||= "\x00"
33
+ opts[:min] ||= 0
34
+
35
+ super(opts)
36
+
37
+ @value = @value.clone if @value
38
+ @value ||= ""
39
+ end
40
+
41
+ # As with Parsel; take a string, return what's left, capture the
42
+ # value in @value.
43
+ #
44
+ def capture(str)
45
+ if @in_size
46
+ max = resolve(@in_size)
47
+ min = resolve(@in_size)
48
+ end
49
+
50
+ max ||= resolve(@max)
51
+ min ||= resolve(@min)
52
+ pad = resolve(@padding)
53
+ nul = resolve(@nul_terminated)
54
+ uni = resolve(@unicode)
55
+ del = resolve(@delimiter)
56
+
57
+ @value = nil
58
+
59
+ incomplete! if not str
60
+
61
+ if (s = size)
62
+ incomplete! if str.size < s
63
+ cap = str[0...s]
64
+ elsif nul
65
+ nterm = str.index(uni ? "\000\000" : "\000")
66
+ if nterm
67
+ cap = str[0...nterm]
68
+ else
69
+ cap = str
70
+ end
71
+ elsif del
72
+ if((idx = str.index(del)))
73
+ cap = str[0...idx]
74
+ else
75
+ cap = str
76
+ end
77
+ else
78
+ cap = str
79
+ end
80
+
81
+ cap = cap[0...max] if max
82
+ while cap.size < min
83
+ cap << pad
84
+ end
85
+
86
+ # must work on a dup of str here or @value may get clobbered by str.slice!
87
+ @value = uni ? cap.to_ascii : cap.dup
88
+
89
+ fin = -1
90
+ mod = nul ? 1 : 0
91
+
92
+ str.slice! 0, (cap.size + mod)
93
+ return str
94
+ end
95
+
96
+ # As per Parsel, write the string
97
+ def to_s(off=nil)
98
+ @rendered_offset = off || 0
99
+
100
+ min = resolve(@min)
101
+ max = resolve(@max)
102
+ uni = resolve(@unicode)
103
+ val = (resolve(@value) || "").to_s
104
+ val = val.clone # gross!
105
+ pad = resolve(@padding)
106
+ nul = resolve(@nul_terminated)
107
+ pto = resolve(@pad_to)
108
+
109
+ val << "\x00" if nul and val[-1] != 0
110
+
111
+ val = val.to_utf16 if uni
112
+
113
+ while min and val.size < min
114
+ val << pad
115
+ end
116
+
117
+ if pto
118
+ while ((val.size % pto) != 0) # this is some shameful shit right here
119
+ val << pad
120
+ end
121
+ end
122
+
123
+ val = val[0...max] if max
124
+
125
+ if off
126
+ return val, off + val.size
127
+ else
128
+ return val
129
+ end
130
+ end
131
+ end
132
+
133
+ ## ---------------------------------------------------------
134
+
135
+ class Asciiz < Str
136
+ def initialize(opts={})
137
+ opts[:nul_terminated] ||= true
138
+ super(opts)
139
+ end
140
+ end
141
+
142
+ ## ---------------------------------------------------------
143
+
144
+ class Unicode < Str
145
+ def initialize(opts={})
146
+ opts[:unicode] ||= true
147
+ super(opts)
148
+ end
149
+ end
150
+
151
+ ## ---------------------------------------------------------
152
+
153
+ class Unicodez < Unicode
154
+ def initialize(opts={})
155
+ opts[:nul_terminated] ||= true
156
+ super(opts)
157
+ end
158
+ end
159
+ Uniz = Unicodez # XXX compat
160
+
161
+ ## ---------------------------------------------------------
162
+
163
+ end
164
+
@@ -0,0 +1,263 @@
1
+ # === Structures are blobs with symbol tables.
2
+ #
3
+
4
+ Dir[File.expand_path("#{File.dirname(__FILE__)}/structure/*.rb")].each do |file|
5
+ require file
6
+ end
7
+
8
+ module Ruckus
9
+ # A Ruckus::Structure wraps a Ruckus::Blob, giving each of the fields
10
+ # a name. Additionally, Structure has classmethod shorthand for DSL-style
11
+ # descriptions of frame/packet formats; see below.
12
+ #
13
+ # Extend Structure by subclassing. Inside the subclass definition,
14
+ # declare fields, like:
15
+ #
16
+ # class Foo < Structure
17
+ # number :width => 32, :endian => :little
18
+ # str :size => 10
19
+ # end
20
+ #
21
+ # Structure catches classmethod calls and converts them to
22
+ # requests to add Parsels to the structure definition.
23
+ #
24
+ # You can inherit indefinitely (each subclass inherits the parent
25
+ # class fields), and you can (obvious) nest --- fields are just
26
+ # parsels.
27
+ #
28
+ class Structure < Parsel
29
+ include StructureInitializers
30
+ include StructureAllowFieldReplacement
31
+ include StructureDefaultValues
32
+ include StructureAtCreate
33
+ include StructureBeforeCallbacks
34
+ include StructureProxies
35
+ include StructureSearchModules
36
+
37
+ class_inheritable_array :templates
38
+ class_inheritable_hash :structure_field_names
39
+
40
+ (class << self;self;end).class_eval {
41
+ include StructureRelateDeclaration
42
+ include StructureValidateField
43
+ include StructureDetectFactory
44
+ include StructureFixupFieldNames
45
+
46
+ def class_method_missing_hook(meth, *args); super; end
47
+ def structure_field_def_hook(*args); super; end
48
+
49
+ # Rules for converting classmethod calls to types:
50
+ # 1. Convert to uppercase
51
+ # 2. If last arg is an opts hash, pull :from, use as module
52
+ # 3. Otherwise, use Ruckus as the module
53
+ # 4. Look up the uppercased name in the module
54
+ # 5. Call #new on it (when the object is instantiated)
55
+ #
56
+ def method_missing(meth, *args)
57
+ begin
58
+ raise "method_missing is recursing" if @mm_locked
59
+ @mm_locked = true
60
+ return if not class_method_missing_hook(meth, *args)
61
+
62
+ if args[-1].kind_of? Hash
63
+ mod = args[-1][:from]
64
+ end
65
+
66
+ structure_field_def_hook args
67
+ # XXX no good hook for this
68
+ mod ||= derive_search_module
69
+
70
+ begin
71
+ klass = mod.const_get(meth.to_s.class_name)
72
+ rescue
73
+ raise "can't find \"#{ meth.to_s.class_name }\" in the default module"
74
+ end
75
+
76
+ add(klass, *args)
77
+ rescue => e
78
+ raise "you called \"#{ meth.to_s }\" on a #{ self.to_s }, and we don't know how to handle that"
79
+ ensure
80
+ @mm_locked = false
81
+ end
82
+ end
83
+ }
84
+
85
+ # If you have an actual class reference, you can just pass
86
+ # it to <tt>add</tt>, as in:
87
+ #
88
+ # add(HeaderStructure, :name => :header)
89
+ #
90
+ def self.add(*args)
91
+ raise "no class" if not args[0]
92
+
93
+ write_inheritable_array :templates, [[args[0], args[1..-1]]]
94
+ end
95
+
96
+ private
97
+ def template_decoder_ring(t)
98
+ # Gross. Fields normally take a first argument, a symbol,
99
+ # specifying the name, and then an opts hash. They can
100
+ # also just take an options hash, in which case we expect
101
+ # the name to be in the hash as :name. Extra fun: you
102
+ # don't have to name every field, and things will still work.
103
+ #
104
+ if t[1][0].kind_of? Symbol and (not t[1][1] || t[1][1].kind_of?(Hash))
105
+ t[1][1] ||= {}
106
+ t[1][1][:name] = (name = t[1][0])
107
+ t[1] = [t[1][1]]
108
+ elsif t[1][0].kind_of? Hash
109
+ name = t[1][0][:name]
110
+ end
111
+ return name
112
+ end
113
+
114
+ def template_entry_added_hook(*args); super *args; end
115
+ def final_initialization_hook(*args); super *args; end
116
+
117
+ public
118
+ # No special options yet. A structure is just a parsel; pass
119
+ # options through to the parent class.
120
+ #f
121
+ def initialize(opts={})
122
+
123
+ # A Structure is really just a Blob with extra goop
124
+ @value = Blob.new
125
+ @value.parent = self
126
+
127
+ # Most of the goop is for figuring out what fields to
128
+ # add, with what arguments, given where we are in the
129
+ # inheritance hierarchy.
130
+
131
+ template = self.class.templates
132
+
133
+ # If this is the first time we're seeing this definition,
134
+ # we also need to convert field names into blob offsets.
135
+ pop = false
136
+ if not self.class.structure_field_names
137
+ self.class.write_inheritable_hash :structure_field_names, {}
138
+ pop = true
139
+ end
140
+
141
+ template.each do |t|
142
+ # do some rewriting to support an old style of declaring
143
+ # fields that we supported like 6 months ago.
144
+ name = template_decoder_ring(t)
145
+
146
+ # index the field name if this is the first time we've
147
+ # ever instantiated this kind of structure, and the field
148
+ # is valid
149
+ self.class.structure_field_names[name] = @value.count if (name and pop)
150
+
151
+ begin
152
+ # create the structure field object, parent it
153
+ klass, args = [t[0], t[1]]
154
+ obj = klass.new(*args)
155
+ obj.parent = @value
156
+
157
+ template_entry_added_hook(obj) || @value << obj
158
+ rescue
159
+ pp t
160
+ raise
161
+ end
162
+ end
163
+
164
+ opts.each do |k, v|
165
+ if self.class.structure_field_names.has_key? k
166
+ raise "attempting to assign field name as option; use with_{field_name} instead"
167
+ end
168
+ end
169
+
170
+ super(opts)
171
+
172
+ final_initialization_hook
173
+ end
174
+
175
+ # Return a field (the Parsel object) by offset into the
176
+ # Structure or by name lookup
177
+ #
178
+ def [](k)
179
+ k = k.intern if k.kind_of? String
180
+ if k.kind_of? Symbol
181
+ @value[self.class.structure_field_names[k]]
182
+ else
183
+ @value[k]
184
+ end
185
+ end
186
+
187
+ # Assign to a field. You can pass scalar types and they'll
188
+ # be converted, so struct.name = "foo" works.
189
+ #
190
+ def []=(k, v)
191
+ k = k.intern if k.kind_of? String
192
+ if k.kind_of? Symbol
193
+ @value[self.class.structure_field_names[k]] = v
194
+ else
195
+ @value[k] = v
196
+ end
197
+ end
198
+
199
+ # Easy --- delegate to blob
200
+ #
201
+ def capture(str)
202
+ @value.capture(str)
203
+ end
204
+
205
+ def before_render_hook(*args); super(*args); end
206
+
207
+ # Easy --- delegate to blob
208
+ #
209
+ def to_s(off=nil)
210
+ before_render_hook
211
+ @rendered_offset = off || 0
212
+ @value.to_s(off)
213
+ end
214
+
215
+ def method_missing_hook(meth, *args); super; end
216
+
217
+ # A la openstruct/struct --- method calls can be references
218
+ # to field names.
219
+ #
220
+ def method_missing(meth, *args)
221
+ return if not method_missing_hook(meth, args)
222
+
223
+ d = self.class.structure_field_names
224
+ m = meth.to_s
225
+
226
+ setter = (m[-1].chr == "=") ? true : false
227
+ m = m[0..-2] if setter
228
+
229
+ puts "WARNING: assignment to @value as struct field" if setter and m == "value"
230
+
231
+ if (i = d[m.intern])
232
+ if setter
233
+ self[m.intern] = args[0]
234
+ else
235
+ self[m.intern]
236
+ end
237
+ else
238
+ super(meth, *args)
239
+ end
240
+ end
241
+
242
+ def self.with_args(*args, &block)
243
+ if args[0].kind_of? Hash
244
+ name = nil
245
+ opts = args[0]
246
+ else
247
+ name = args[0]
248
+ opts = args[1]
249
+ end
250
+
251
+ opts ||= {}
252
+ block.call(name, opts)
253
+ end
254
+
255
+ def each_field
256
+ @value.each {|f| yield f.name, f}
257
+ end
258
+ end
259
+ end
260
+
261
+ # Read me for the current list of field definitions shortcuts.
262
+ load File.dirname(__FILE__) + '/structure_shortcuts.rb'
263
+
@@ -0,0 +1,23 @@
1
+ module Ruckus
2
+ module StructureAtCreate
3
+ def self.included(klass)
4
+ klass.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def at_create(arg=nil, &block)
9
+ if not block_given?
10
+ raise "need a callback function" if not arg
11
+ arg = arg.intern if not arg.kind_of? Symbol
12
+ block = lambda { send(arg) }
13
+ end
14
+
15
+ self.initializers << block
16
+ end
17
+
18
+ def override(field, val)
19
+ at_create { self[field] = val }
20
+ end
21
+ end
22
+ end
23
+ end