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.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/README +0 -0
- data/README.markdown +51 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/ruckus.rb +70 -0
- data/lib/ruckus/blob.rb +113 -0
- data/lib/ruckus/choice.rb +55 -0
- data/lib/ruckus/dfuzz.rb +231 -0
- data/lib/ruckus/dictionary.rb +119 -0
- data/lib/ruckus/enum.rb +39 -0
- data/lib/ruckus/extensions/array.rb +33 -0
- data/lib/ruckus/extensions/class.rb +168 -0
- data/lib/ruckus/extensions/duplicable.rb +37 -0
- data/lib/ruckus/extensions/file.rb +24 -0
- data/lib/ruckus/extensions/fixnum.rb +26 -0
- data/lib/ruckus/extensions/hash.rb +10 -0
- data/lib/ruckus/extensions/integer.rb +26 -0
- data/lib/ruckus/extensions/ipaddr.rb +155 -0
- data/lib/ruckus/extensions/irb.rb +30 -0
- data/lib/ruckus/extensions/math.rb +6 -0
- data/lib/ruckus/extensions/module.rb +37 -0
- data/lib/ruckus/extensions/numeric.rb +117 -0
- data/lib/ruckus/extensions/object.rb +22 -0
- data/lib/ruckus/extensions/range.rb +16 -0
- data/lib/ruckus/extensions/socket.rb +20 -0
- data/lib/ruckus/extensions/string.rb +327 -0
- data/lib/ruckus/filter.rb +16 -0
- data/lib/ruckus/human_display.rb +79 -0
- data/lib/ruckus/ip.rb +38 -0
- data/lib/ruckus/mac_addr.rb +31 -0
- data/lib/ruckus/mutator.rb +318 -0
- data/lib/ruckus/null.rb +24 -0
- data/lib/ruckus/number.rb +360 -0
- data/lib/ruckus/parsel.rb +363 -0
- data/lib/ruckus/selector.rb +92 -0
- data/lib/ruckus/str.rb +164 -0
- data/lib/ruckus/structure.rb +263 -0
- data/lib/ruckus/structure/atcreate.rb +23 -0
- data/lib/ruckus/structure/beforebacks.rb +28 -0
- data/lib/ruckus/structure/defaults.rb +57 -0
- data/lib/ruckus/structure/factory.rb +42 -0
- data/lib/ruckus/structure/fixupfields.rb +16 -0
- data/lib/ruckus/structure/initializers.rb +14 -0
- data/lib/ruckus/structure/relate.rb +34 -0
- data/lib/ruckus/structure/replacement.rb +30 -0
- data/lib/ruckus/structure/searchmods.rb +33 -0
- data/lib/ruckus/structure/structureproxies.rb +28 -0
- data/lib/ruckus/structure/validate.rb +12 -0
- data/lib/ruckus/structure_shortcuts.rb +109 -0
- data/lib/ruckus/time_t.rb +26 -0
- data/lib/ruckus/vector.rb +97 -0
- data/ruckus.gemspec +104 -0
- data/test/test_decides.rb +61 -0
- data/test/test_mutator.rb +29 -0
- data/test/test_override.rb +23 -0
- data/test/test_replace.rb +39 -0
- 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
|
+
|
data/lib/ruckus/ip.rb
ADDED
@@ -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
|