ruckus 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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,363 @@
1
+ # === Parsels are tree nodes
2
+
3
+ module Ruckus
4
+ class IncompleteCapture < RuntimeError; end
5
+
6
+ # A parsel is an object that supports the following three methods:
7
+ # * An <tt>initialize</tt> that accepts and passes through an opts hash
8
+ # * A <tt>to_s<tt> that renders binary, and takes an optional "offset" arg
9
+ # * A <tt>capture</tt> that parses a binary string and returns whatever
10
+ # is left of the strin.
11
+ #
12
+ # You assemble a tree of Parsel objects to make a packet. The parsel
13
+ # tree winds up looking a lot like the HTML DOM:
14
+ # * There's a tree root you can get to from anywhere by calling <tt>root</tt>
15
+ # * Nodes can have DOM-style "classes" (we call them "names")
16
+ # * Nodes can have DOM-style "ids" (we call them "tags")
17
+ #
18
+ # As the tree is rendered (by calling to_s on every node), the offset
19
+ # in the final string is stored in @rendered_offset.
20
+ #
21
+ # All this is useful for instance with binary formats that required padded
22
+ # offsets from headers --- tag the header base, look it up from anywhere,
23
+ # compare rendered offsets, and you know how much padding you need.
24
+ #
25
+ # Any attribute of a Parsel can be replaced with:
26
+ # * A method to call on a sibling node to get the value (for instance,
27
+ # the size of a string)
28
+ # * A complex specification of which node to query, what method to use,
29
+ # and how to modify it
30
+ # * A block
31
+ #
32
+ class Parsel
33
+ attr_accessor :value
34
+ attr_accessor :parent
35
+ attr_accessor :rendered_offset
36
+ attr_accessor :tag
37
+ attr_accessor :name
38
+ attr_accessor :rendering
39
+
40
+ # Is this parsel in native byte order?
41
+ #
42
+ def native?
43
+ self.class.native @endian || :little
44
+ end
45
+
46
+ # What's our native endianness? :big or :little
47
+ #
48
+ def self.endian?
49
+ @endianness ||= ([1].pack("I")[0] == 1 ? :little : :big)
50
+ end
51
+
52
+ # Is this endianness native?
53
+ #
54
+ def self.native?(endian)
55
+ endian? == endian
56
+ end
57
+
58
+ # How many bytes, rounded up, does it take to represent this
59
+ # many bits?
60
+ #
61
+ def self.bytes_for_bits(b)
62
+ (b / 8) + ((b % 8) != 0 ? 1 : 0)
63
+ end
64
+
65
+ # Coerce native types to their equivalent parsels, so you can
66
+ # assign "1" to a Number field, etc
67
+ #
68
+ def self.coerce(val)
69
+ if val.kind_of? Numeric
70
+ Number.new :value => val
71
+ elsif val.kind_of? String
72
+ Str.new :value => val
73
+ end
74
+ end
75
+
76
+ # Read Blob first.
77
+ #
78
+ # Parsels can have nonscalar values, including blocks and
79
+ # references to the values of other fields. This is a bit
80
+ # of a mess. Takes:
81
+ #
82
+ # all:: Rebind all instance variables, not just @value
83
+ # meth:: Method to call on target object to extract value,
84
+ # but note that you can just pass "val" as a sym
85
+ # for same effect. (Default: :size)
86
+ # source:: Source Parsel to extract object, but you'll never
87
+ # specify this directly.
88
+ # One exception: :source => :rest means, "apply the
89
+ # method to all subsequent elements of the blob or
90
+ # structure"
91
+ # offset:: (Default: 1) which neighboring Parsel should we
92
+ # extract from --- can also be <tt>:this</tt>,
93
+ # <tt>:prev</tt>, and <tt>:next</tt>.
94
+ # block:: A Proc to call with the object we're extracting.
95
+ #
96
+ def resolve(val)
97
+ return nil if not val
98
+ return val if [String, Integer, Ruckus::Mutator::Mutator].kind_of_these? val
99
+ return val if val == true
100
+ return val if val == false
101
+
102
+ o = {}
103
+
104
+ if val.kind_of? Symbol
105
+ o[:meth] = val
106
+ elsif val.kind_of? Hash
107
+ o = o.merge(val)
108
+ end
109
+
110
+ if (t = @from_tag) || (t = o[:from_tag])
111
+ o[:source] = root.find_tag(t)
112
+ raise "can't find" if not o[:source]
113
+ end
114
+
115
+ if (f = @from_field) || (f = o[:from_field])
116
+ o[:source] = parent_struct.send f
117
+ raise "can't find field" if not o[:source]
118
+ end
119
+
120
+ place = parent.place(self)
121
+
122
+ if not o[:source]
123
+ raise "unparented" if not parent
124
+
125
+ o[:offset] ||= 1
126
+ o[:offset] = 0 if o[:offset] == :this
127
+ o[:offset] = -1 if o[:offset] == :prev
128
+ o[:offset] = 1 if o[:offset] == :next
129
+
130
+ loc = place + o[:offset]
131
+ o[:source] = parent[loc]
132
+
133
+ raise "can't resolve #{ o } for #{ @name }" if not o[:source]
134
+ end
135
+
136
+ if not o[:block]
137
+ o[:meth] ||= :size
138
+
139
+ if o[:source] == :rest
140
+ r = 0
141
+ ((place+1)..(parent.size)).each do |i|
142
+ r += parent[i].send(o[:meth]) if parent[i]
143
+ end
144
+ else
145
+ r = o[:source].send o[:meth]
146
+ end
147
+ else
148
+ r = o[:block].call o[:source]
149
+ end
150
+
151
+ # cheat: if resolution returns a symbol --- which happens
152
+ # with len/string pairs, because they depend on each other ---
153
+ # return nil. This effectively unbounds the string during to_s.
154
+ r = nil if r.kind_of? Symbol
155
+
156
+ r = @modifier.call(self, r) if @modifier
157
+ return r
158
+ end
159
+
160
+ # Get to the next node (at this level of the tree --- does
161
+ # not traverse back through parent)
162
+ #
163
+ def next
164
+ parent[parent.place(self) + 1]
165
+ end
166
+
167
+ # Opposite of Parsel#next
168
+ #
169
+ def prev
170
+ parent[parent.place(self) - 1]
171
+ end
172
+
173
+ # Walk up the parents of this node until we find a containing
174
+ # structure.
175
+ #
176
+ def parent_structure(p = self)
177
+ while p.parent
178
+ p = p.parent
179
+ break if p.kind_of? Ruckus::Structure
180
+ end
181
+ return p
182
+ end
183
+ alias_method :parent_struct, :parent_structure
184
+
185
+ VERBOTEN = [:size, :capture]
186
+
187
+ # Note: all opts become instance variables. Never created
188
+ # directly.
189
+ #
190
+ def initialize(opts={})
191
+ (rec = lambda do |k|
192
+ begin
193
+ k.const_get(:OPTIONS).each do |key, v|
194
+ opts[key] ||= v
195
+ end
196
+ rescue
197
+ ensure
198
+ rec.call(k.superclass) if k.inherits_from? Parsel
199
+ end
200
+ end).call(self.class)
201
+
202
+ opts.each do |k, v|
203
+ next if VERBOTEN.member? k
204
+
205
+ instance_variable_set "@#{ k }".intern, v
206
+ (class << self; self; end).instance_eval {
207
+ attr_accessor k
208
+ }
209
+ end
210
+
211
+ capture(opts[:capture]) if opts[:capture]
212
+ end
213
+
214
+ # How big is the rendered output in bytes? By default,
215
+ # the worst possible impl: render and take size of result.
216
+ # You can override to make this more reasonable.
217
+ #
218
+ def size
219
+ return nil if not @value
220
+ return to_s.size
221
+ end
222
+
223
+ # Parsel decorates native types.
224
+ #
225
+ def method_missing(meth, *args, &block)
226
+ @value.send meth, *args, &block
227
+ end
228
+
229
+ # Traverse all the way to the root of the tree
230
+ #
231
+ def root(p = self, &block)
232
+ yield p if block_given?
233
+ while p.parent
234
+ p = p.parent
235
+ yield p if block_given?
236
+ end
237
+ return p
238
+ end
239
+
240
+ # Stubbed.
241
+ #
242
+ def fixup
243
+ end
244
+
245
+ # Find any node by its tag. This isn't indexed in any way, so
246
+ # it's slow, but who cares?
247
+ #
248
+ def find_tag(t)
249
+ r = nil
250
+ if @tag == t
251
+ r = self
252
+ elsif
253
+ begin
254
+ each do |it|
255
+ r = it.find_tag(t)
256
+ break if r
257
+ end
258
+ rescue
259
+ end
260
+ end
261
+ return r
262
+ end
263
+
264
+ # Find a node by its tag, but return its enclosing structure, not
265
+ # the node itself. This is usually what you want; the first field
266
+ # of a header might be tagged, but you just wanted a reference to
267
+ # the header entire.
268
+ #
269
+ def find_tag_struct(t)
270
+ p = find_tag(t)
271
+ if(p)
272
+ return p.parent_struct
273
+ else
274
+ return nil
275
+ end
276
+ end
277
+
278
+ # Walk up parents until you find one of type <tt>klass</tt>;
279
+ # so, if you have a FooHeader containing lots of FooRecords,
280
+ # and you have a handle on a FooElement inside a FooRecord, you
281
+ # can call <tt>x.find_containing(FooRecord)</tt> to jump right
282
+ # back to the header.
283
+ #
284
+ def find_containing(klass)
285
+ p = parent
286
+ while p and not p.kind_of? klass
287
+ p = p.parent
288
+ end
289
+ return p
290
+ end
291
+
292
+ # Wrap tree-traversal; <tt>&block</tt> is called for each
293
+ # element of the tree.
294
+ #
295
+ def visit(&block)
296
+ raise "need block" if not block_given?
297
+
298
+ block.call(self)
299
+
300
+ begin
301
+ @value.each do |o|
302
+ o.visit(&block)
303
+ end
304
+ rescue
305
+ end
306
+ end
307
+
308
+ # This kind of doesn't belong here. XXX factor out into
309
+ # module.
310
+ #
311
+ # Use Parsel#visit to permute every permutable Parsel in
312
+ # a message, by calling value.permute (Mutator objects
313
+ # respond to this message). See Mutate.rb.
314
+ #
315
+ def permute(all=nil)
316
+ visit {|o| o.permute(nil)} and return if all
317
+ begin
318
+ @value.permute
319
+ rescue
320
+ end
321
+ end
322
+
323
+ # See Blob.
324
+ #
325
+ # What's our position in the parent? This works for any
326
+ # enumerable parent.
327
+ #
328
+ def where_am_i?
329
+ return @where_am_i if @where_am_i
330
+
331
+ raise "not parented" if not parent
332
+
333
+ parent.each_with_index do |o, i|
334
+ if o == self
335
+ @where_am_i = i
336
+ return i
337
+ end
338
+ end
339
+ nil
340
+ end
341
+
342
+ # This got insane following links, so cut down what 'pp' gives you,
343
+ # mostly by not recursively rendering children.
344
+ #
345
+ def inspect
346
+ if @value.kind_of? Parsel
347
+ val = "#<#{ @value.class }:#{ @value.object_id }: @name=\"#{ @name }\">"
348
+ else
349
+ val = @value.inspect
350
+ end
351
+
352
+ "#<#{ self.class }:#{ self.object_id }: @name=\"#{ @name }\" @value=#{ val }>"
353
+ end
354
+
355
+ # phase in the new names
356
+ def out(*args); to_s(*args); end
357
+ def in(*args); capture(*args); end
358
+
359
+ def self.factory?; false; end
360
+
361
+ def incomplete!; raise IncompleteCapture; end
362
+ end
363
+ end
@@ -0,0 +1,92 @@
1
+ module Ruckus
2
+ class Selector < String
3
+ attr_reader :rid
4
+ attr_reader :rclass
5
+ attr_reader :rkind
6
+
7
+ def initialize(s)
8
+ if s =~ /(?:([^.#]+))?(?:\.([^.#]+))?(?:#(.+))?/
9
+ @rkind, @rclass, @rid = [$1, $2, $3]
10
+ else
11
+ @rkind, @rclass, @rid = [nil, nil, s]
12
+ end
13
+
14
+ super
15
+ end
16
+ end
17
+
18
+ class Parsel
19
+ def index_for_selectors
20
+ b = lambda {|h, k| h[k] = []}
21
+ ids, classes, kinds = [Hash.new(&b), Hash.new(&b), Hash.new(&b)]
22
+
23
+ visit do |n|
24
+ k = n.class
25
+ while k != Ruckus::Parsel and k != Object
26
+ kinds[k.to_s] << n
27
+ k = k.superclass
28
+ end
29
+
30
+ (ids[n.tag.to_s] << n) if n.tag
31
+ (classes[n.name.to_s] << n) if n.name
32
+ end
33
+
34
+ @selector_index = [ids, classes, kinds]
35
+ end
36
+
37
+ def each_matching_selector(sel)
38
+ index_for_selectors if not @selector_index
39
+ sels = sel.split.reverse.map {|x| Selector.new(x)}
40
+
41
+ first = sels.shift
42
+
43
+ ipool = first.rid ? @selector_index[0][first.rid] : nil
44
+ cpool = first.rclass ? @selector_index[1][first.rclass] : nil
45
+ kpool = first.rkind ? @selector_index[2][first.rkind] : nil
46
+
47
+ pool = ipool || cpool || kpool
48
+ pool = (pool & ipool) if ipool
49
+ pool = (pool & cpool) if cpool
50
+ pool = (pool & kpool) if kpool
51
+
52
+ # XXX this loop is fucked:
53
+ # outer loop needs to be pool
54
+ # will capture parent nodes in arbitrary order
55
+ # sels.each do |s|
56
+ # pool.each do |victim|
57
+ # found = false
58
+ # victim.root {|n| found = true if n.matches_selector? s}
59
+ # pool.delete(victim) if not found
60
+ # end
61
+ # end
62
+
63
+ pool.each do |victim|
64
+ tmpsels = sels.clone
65
+ cur = sels.shift
66
+
67
+ victim.root do |parent|
68
+ break if not cur
69
+ if parent.matches_selector? cur
70
+ cur = sels.shift
71
+ end
72
+ end
73
+
74
+ pool.delete(victim) if cur
75
+ end
76
+
77
+ pool.each do |n|
78
+ yield n
79
+ end
80
+
81
+ pool.size
82
+ end
83
+
84
+ def matches_selector?(sel)
85
+ index_for_selectors if not @selector_index
86
+ return false if sel.rid and sel.rid != self.tag
87
+ return false if sel.rclass and sel.rclass != self.name
88
+ return false if sel.rkind and not @selector_index[2][sel.rkind].include? self
89
+ return true
90
+ end
91
+ end
92
+ end