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,327 @@
1
+ %w[iconv stringio].each {|x| require x}
2
+
3
+ class String
4
+ module StringExtensions
5
+ # Convert a string to "Unicode", ie, the way Win32 expects it, including
6
+ # trailing NUL.
7
+ def to_utf16
8
+ Iconv.iconv("utf-16LE", "utf-8", self).first + "\x00\x00"
9
+ end
10
+
11
+ # Convert a "Unicode" (Win32-style) string back to native Ruby UTF-8;
12
+ # get rid of any trailing NUL.
13
+ def from_utf16
14
+ ret = Iconv.iconv("utf-8", "utf-16le", self).first
15
+ if ret[-1] == 0
16
+ ret = ret[0..-2]
17
+ end
18
+ end
19
+ alias_method :to_utf8, :from_utf16
20
+ alias_method :to_ascii, :from_utf16
21
+
22
+ # Convenience for parsing UNICODE strings from a buffer
23
+ # Assumes last char ends in 00, which is not always true but works in English
24
+ def from_utf16_buffer
25
+ self[0..index("\0\0\0")+2].from_utf16
26
+ end
27
+
28
+ # Sometimes string buffers passed through Win32 interfaces come with
29
+ # garbage after the trailing NUL; this method gets rid of that, like
30
+ # String#trim
31
+ def asciiz
32
+ begin
33
+ self[0..self.index("\x00")-1]
34
+ rescue
35
+ self
36
+ end
37
+ end
38
+
39
+ # My entry into the hexdump race. Outputs canonical hexdump, uses
40
+ # StringIO for speed, could be cleaned up with "ljust", and should
41
+ # probably use table lookup instead of to_s(16) method calls.
42
+ def hexdump(capture=false)
43
+ sio = StringIO.new
44
+ rem = size - 1
45
+ off = 0
46
+
47
+ while rem > 0
48
+ pbuf = ""
49
+ pad = (15 - rem) if rem < 16
50
+ pad ||= 0
51
+
52
+ sio.write(("0" * (8 - (x = off.to_s(16)).size)) + x + " ")
53
+
54
+ 0.upto(15-pad) do |i|
55
+ c = self[off]
56
+ x = c.to_s(16)
57
+ sio.write(("0" * (2 - x.size)) + x + " ")
58
+ if c.printable?
59
+ pbuf << c
60
+ else
61
+ pbuf << "."
62
+ end
63
+ off += 1
64
+ rem -= 1
65
+ sio.write(" ") if i == 7
66
+ end
67
+
68
+ sio.write("-- " * pad) if pad > 0
69
+ sio.write(" |#{ pbuf }|\n")
70
+ end
71
+
72
+ sio.rewind()
73
+ if capture
74
+ sio.read()
75
+ else
76
+ puts sio.read()
77
+ end
78
+ end
79
+
80
+ # convert a string to its idiomatic ruby class name
81
+ def class_name
82
+ r = ""
83
+ up = true
84
+ each_byte do |c|
85
+ if c == 95
86
+ if up
87
+ r << "::"
88
+ else
89
+ up = true
90
+ end
91
+ else
92
+ m = up ? :upcase : :to_s
93
+ r << (c.chr.send(m))
94
+ up = false
95
+ end
96
+ end
97
+ r
98
+ end
99
+
100
+ # Insane that this isn't in the library by default.
101
+ def starts_with? x
102
+ self[0..x.size-1] == x
103
+ end
104
+
105
+ def ends_with? x
106
+ self[-(x.size)..-1] == x
107
+ end
108
+
109
+ # Cribbed from Ero Carrera's pefile; a relatively expensive entropy
110
+ # function, gives a float result of random-bits-per-byte.
111
+ def entropy
112
+ e = 0
113
+ 0.upto(255) do |i|
114
+ x = count(i.chr)/size.to_f
115
+ if x > 0
116
+ e += - x * Math.log2(x)
117
+ end
118
+ end
119
+
120
+ return e
121
+ end
122
+
123
+ # The driver function for String#strings below; really, this will
124
+ # run on any Enumerable that contains Fixnums.
125
+ def nextstring(opts={})
126
+ off = opts[:offset] || 0
127
+ sz = opts[:minimum] || 7
128
+ u = opts[:unicode] || false
129
+ l = size
130
+ i = off
131
+ while i < l
132
+ if self[i].printable?
133
+ start = i
134
+ cnt = 1
135
+ i += 1
136
+ lastu = false
137
+ while i < l
138
+ if self[i].printable?
139
+ lastu = false
140
+ cnt += 1
141
+ i += 1
142
+ elsif u and self[i] == 0 and not lastu
143
+ lastu = true
144
+ i += 1
145
+ else
146
+ break
147
+ end
148
+ end
149
+
150
+ return([start, i - start]) if cnt >= sz
151
+ else
152
+ i += 1
153
+ end
154
+ end
155
+
156
+ return false, false
157
+ end
158
+
159
+ # A la Unix strings(1). With a block, yields offset, string length,
160
+ # and contents. Otherwise returns a list. Accepts options:
161
+ # :unicode: superficial but effective Win32 Unicode support, skips NULs
162
+ # :minimum: minimum length of returned strings, ala strings -10
163
+ def strings(opts={})
164
+ ret = []
165
+ opts[:offset] ||= 0
166
+ while 1
167
+ off, size = nextstring(opts)
168
+ break if not off
169
+ opts[:offset] += (off + size)
170
+ if block_given?
171
+ yield off, size, self[off,size]
172
+ else
173
+ ret << [off, size, self[off,size]]
174
+ end
175
+ end
176
+ ret
177
+ end
178
+
179
+ # A hacked up adler16 checksum, a la Andrew Tridgell. This is probably
180
+ # even slower than Ruby's native CRC support. A weak, trivial checksum,
181
+ # part of rsync.
182
+ def adler
183
+ a, b = 0, 0
184
+ 0.upto(size-1) {|i| a += self[i]}
185
+ a %= 65536
186
+ 0.upto(size-1) {|i| b += ((size-i)+1) * self[i]}
187
+ b %= 65536
188
+ return (a|(b<<16))
189
+ end
190
+
191
+ # Convert binary strings back to integers
192
+ def to_l32; unpack("L").first; end
193
+ def to_b32; unpack("N").first; end
194
+ def to_l16; unpack("v").first; end
195
+ def to_b16; unpack("n").first; end
196
+ def to_u8; self[0]; end
197
+ def shift_l32; shift(4).to_l32; end
198
+ def shift_b32; shift(4).to_b32; end
199
+ def shift_l16; shift(2).to_l16; end
200
+ def shift_b16; shift(2).to_b16; end
201
+ def shift_u8; shift(1).to_u8; end
202
+
203
+ # oh, it's exactly what it sounds like.
204
+ def method_name
205
+ r = ""
206
+ scoped = false
207
+ each_byte do |c|
208
+ if c == 58
209
+ if not scoped
210
+ r << "_"
211
+ scoped = true
212
+ else
213
+ scoped = false
214
+ end
215
+ else
216
+ if r.size == 0
217
+ r << c.chr.downcase
218
+ else
219
+ if c.upper?
220
+ r << "_"
221
+ r << c.chr.downcase
222
+ else
223
+ r << c.chr
224
+ end
225
+ end
226
+ end
227
+ end
228
+ return r
229
+ end
230
+
231
+ # I love you String#ljust
232
+ def pad(size, char="\x00")
233
+ ljust(size, char)
234
+ end
235
+
236
+ # Convert a string into hex characters
237
+ def hexify
238
+ l = []
239
+ each_byte{|b| l << "%02x" % b}
240
+ l.join
241
+ end
242
+
243
+ # convert a string to hex characters in place
244
+ def hexify!
245
+ self.replace hexify
246
+ end
247
+
248
+
249
+ # Convert a string of raw hex characters (no %'s or anything) into binary
250
+ def dehexify
251
+ (ret||="") << (me||=clone).shift(2).to_i(16).chr while not (me||=clone).empty?
252
+ return ret
253
+ end
254
+
255
+ # Convert a string of raw hex characters (no %'s or anything) into binary in place
256
+ def dehexify!
257
+ (ret||="") << (me||=clone).shift(2).to_i(16).chr while not (me||=clone).empty?
258
+ self.replace ret
259
+ end
260
+
261
+ # OR two strings together. Slow. Handles mismatched lengths by zero-extending
262
+ def or(str)
263
+ max = size < str.size ? str.size : size
264
+ ret = ""
265
+ 0.upto(max-1) do |i|
266
+ x = self[i] || 0
267
+ y = str[i] || 0
268
+ ret << (x | y).chr
269
+ end
270
+ return ret
271
+ end
272
+
273
+ # XOR two strings. wrapping around if str is shorter thn self.
274
+ def xor(str)
275
+ r = []
276
+ size.times do |i|
277
+ r << (self[i] ^ str[i % str.size]).chr
278
+ end
279
+ return r.join
280
+ end
281
+
282
+ def xor!(str)
283
+ size.times do |i|
284
+ self[i] ^= str[i % str.size]
285
+ end
286
+ end
287
+
288
+ # byte rotation cypher (yes it's been useful)
289
+ def rotate_bytes(k=0)
290
+ # XXX not used
291
+ r = []
292
+ each_byte do |b|
293
+ r << ((b + k) % 256).chr
294
+ end
295
+ return r.join
296
+ end
297
+
298
+ # Insanely useful shorthand: pop bytes off the front of a string
299
+ def shift(count=1)
300
+ return self if count == 0
301
+ slice! 0..(count-1)
302
+ end
303
+
304
+ def underscore
305
+ first = false
306
+ gsub(/[a-z0-9][A-Z]/) do |m|
307
+ "#{ m[0].chr }_#{ m[1].chr.downcase }"
308
+ end
309
+ end
310
+
311
+ # "foo: bar".shift_tok /:\s*/ => "foo" # leaving "bar"
312
+ def shift_tok(rx)
313
+ # XXX not used
314
+ src = rx.source if rx.kind_of? Regexp
315
+ rx = Regexp.new "(#{ src })"
316
+ idx = (self =~ rx)
317
+ if idx
318
+ ret = shift(idx)
319
+ shift($1.size)
320
+ return ret
321
+ else
322
+ shift(self.size)
323
+ end
324
+ end
325
+ end
326
+ include StringExtensions
327
+ end
@@ -0,0 +1,16 @@
1
+ module Ruckus
2
+ class Filter < Parsel
3
+ def initialize(val, &block)
4
+ raise "need a block" if not block_given?
5
+ val = val.clone
6
+ val.parent = self
7
+ opts = { :value => val }
8
+ @block = block
9
+ super(opts)
10
+ end
11
+
12
+ def to_s
13
+ @block.call(@value.to_s)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,79 @@
1
+ ## Human display methods
2
+ # Here are various methods for ruckus base types to display human-readable
3
+ # data representations. No particular attempt is made at efficiency. These
4
+ # are for dissectors, analysis, debugging, etc...
5
+
6
+ module Ruckus
7
+ Blob.class_eval do
8
+ def to_human(indent=nil)
9
+ indent ||= ""
10
+ vals = []
11
+ vals << "#{indent}#{name.to_s} = " if @name
12
+ indent += " "
13
+ vals += @value.map do |it|
14
+ if it.respond_to?(:format) and it.format.kind_of? Proc
15
+ "#{indent}#{it.name} = #{it.format.call(it)}"
16
+ elsif it.respond_to?(:to_human)
17
+ it.to_human(indent)
18
+ else
19
+ "#{indent}#{it.name} = #{it.value}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+ Structure.class_eval do
27
+ def to_human(indent=nil)
28
+ "#{ indent || '' }#{self.class}".to_a + super(indent)
29
+ end
30
+ end
31
+
32
+
33
+ IP.class_eval do
34
+ def to_human(indent=nil)
35
+ s = to_s
36
+ "#{ indent || '' }#{name.to_s} = #{s[0]}.#{s[1]}.#{s[2]}.#{s[3]}"
37
+ end
38
+ end
39
+
40
+
41
+ Choice.class_eval do
42
+ def to_human(indent=nil)
43
+ indent ||= ''
44
+ head = "#{indent}#{name.to_s} = "
45
+ if (val=@value.to_human(indent)).kind_of?(Array) and @name
46
+ val.unshift head
47
+ else
48
+ head + val.to_s.strip
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ Str.class_eval do
55
+ def to_human(indent=nil)
56
+ "#{ indent || '' }#{@name} = " +
57
+ (@value.empty? ? "<empty>" : "\n%%\n#{@value.hexdump(:out => StringIO.new)}%%\n")
58
+ end
59
+ end
60
+
61
+
62
+ Number.class_eval do
63
+ def to_human(indent=nil)
64
+ if self.value.kind_of?(Numeric) then
65
+ "#{ indent ||'' }#{@name} = %d (0x%x)" %[ self.value, self.value ]
66
+ else
67
+ "#{ indent ||'' }#{@name} = #{self.value}"
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ Null.class_eval do
74
+ def to_human(indent=nil)
75
+ "#{ indent || '' }#{@name} = <nil>"
76
+ end
77
+ end
78
+ end
79
+
@@ -0,0 +1,38 @@
1
+ # === IP addresses are integers with special assignment
2
+ # You can use a Ruckus::Number as an IP address (or an IPv6
3
+ # address, if you make it 128 bits wide), but Ruckus::IP
4
+ # converts from dotted-quad strings (and, I guess if I wanted,
5
+ # DNS hostnames)
6
+ #
7
+ module Ruckus
8
+ class IP < Parsel
9
+ def initialize(opts={})
10
+ opts[:value] ||= 0
11
+ super(opts)
12
+ end
13
+
14
+ def to_s(off=nil)
15
+ @rendered_offset = off || 0
16
+
17
+ val = resolve(@value)
18
+ val = IPAddr.inet_addr(val) if val.kind_of? String
19
+ r = [val].pack("I")
20
+ r = r.reverse if self.class.endian? == :little
21
+
22
+ if off
23
+ return r, off + 4
24
+ else
25
+ return r
26
+ end
27
+ end
28
+
29
+ def capture(str)
30
+ cap = str.shift 4
31
+ sel = self.class.endian? == :little ? :reverse : :to_s
32
+ @value = cap.send(sel).unpack("I").first
33
+ return str
34
+ end
35
+
36
+ def size; 4; end
37
+ end
38
+ end