emonti-rbkb 0.6.1.3 → 0.6.2

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.
@@ -0,0 +1,102 @@
1
+ require 'rbkb/cli'
2
+
3
+ # rstrings is Unix "strings" in ruby... with some extra stuff
4
+ class Rbkb::Cli::Rstrings < Rbkb::Cli::Executable
5
+ def initialize(*args)
6
+ super(*args)
7
+ {
8
+ :start_off => 0,
9
+ :end_off => -1,
10
+ :encoding => :both,
11
+ :minimum => 6,
12
+ :align => nil,
13
+ :indat => Array.new,
14
+ :fnames => Array.new,
15
+ }.each {|k,v| @opts[k] ||= v }
16
+ end
17
+
18
+ def make_parser()
19
+ arg = super()
20
+ arg.banner += " <file ... || blank for stdin>"
21
+
22
+ arg.on("-s", "--start=OFFSET", "Start at offset") do |s|
23
+ unless m=/^(?:(\d+)|0x([A-Fa-f0-9]+))$/.match(s)
24
+ bail "invalid offset '#{s}'"
25
+ end
26
+ @opts[:start_off] = (m[2])? m[0].hex : m[0].to_i
27
+ end
28
+
29
+ arg.on("-e", "--end=OFFSET", "End at offset") do |e|
30
+ unless m=/^(?:(\d+)|0x([A-Fa-f0-9]+))$/.match(e)
31
+ bail "invalid offset '#{e}'"
32
+ end
33
+ @opts[:end_off] = (m[2])? m[0].hex : m[0].to_i
34
+ end
35
+
36
+ arg.on("-t", "--encoding-type=TYPE",
37
+ "Encoding: ascii/unicode/both (default=#{@opts[:encoding]})") do |t|
38
+ @opts[:encoding] = t.to_sym
39
+ end
40
+
41
+ arg.on("-l", "--min-length=NUM", Numeric,
42
+ "Minimum length of strings (default=#{@opts[:minimum]})") do |l|
43
+ @opts[:minimum] = l
44
+ end
45
+
46
+ arg.on("-a", "--align=ALIGNMENT", Numeric,
47
+ "Match only on alignment (default=none)") do |a|
48
+ (@opts[:align] = a) > 0 or bail "bad alignment '#{a}'"
49
+ end
50
+
51
+ return arg
52
+ end
53
+
54
+ def parse(*args)
55
+ super(*args)
56
+ if @opts[:indat].empty? and not @argv.empty?
57
+ while a=@argv.shift
58
+ @opts[:indat] << do_file_read(a)
59
+ @opts[:fnames] << a
60
+ end
61
+ end
62
+
63
+ parse_catchall()
64
+
65
+ if @opts[:indat].empty?
66
+ @opts[:indat] << @stdin.read() if @opts[:indat].empty?
67
+ @opts[:fnames] << "[STDIN]"
68
+ end
69
+ end
70
+
71
+ def go(*args)
72
+ super(*args)
73
+
74
+ start_off = @opts[:start_off]
75
+ end_off = @opts[:end_off]
76
+ enc = @opts[:encoding]
77
+ min = @opts[:minimum]
78
+ align = @opts[:align]
79
+
80
+ @opts[:pr_fnames]=true if @opts[:fnames].size > 1
81
+
82
+ i=0
83
+ while buf=@opts[:indat].shift
84
+ buf[start_off..end_off].strings(
85
+ :encoding => enc,
86
+ :minimum => min,
87
+ :align => align
88
+ ) do |off, len, type, str|
89
+ if @opts[:pr_fnames]
90
+ @stdout << "#{@opts[:fnames][i]}:"
91
+ end
92
+ @stdout << "#{(off+start_off).to_hex.rjust(8,"0")}:"+
93
+ "#{(len+start_off).to_hex.rjust(8,"0")}:"+
94
+ "#{type.to_s[0,1]}:#{str.delete("\000").inspect}\n"
95
+ end
96
+ i+=1
97
+ end
98
+
99
+ self.exit(0)
100
+ end
101
+ end
102
+
@@ -0,0 +1,43 @@
1
+ require 'rbkb/cli'
2
+
3
+ # Returns a slice from input. This is just a shell interface to a String.slice
4
+ # operation.
5
+ class Rbkb::Cli::Slice < Rbkb::Cli::Executable
6
+
7
+ def initialize(*args)
8
+ super(*args)
9
+ @opts[:last] ||= -1
10
+ end
11
+
12
+ def make_parser()
13
+ super()
14
+ add_std_file_opt(:indat)
15
+ add_range_opts(:first, :last)
16
+ arg = @oparse
17
+
18
+ arg.banner += " start (no args when using -r or -x)"
19
+ end
20
+
21
+
22
+ def parse(*args)
23
+ super(*args)
24
+ @opts[:first] ||= @argv.shift
25
+
26
+ unless(Numeric === @opts[:first] or /^-?\d+$/.match(@opts[:first]) )
27
+ bail_args "invalid start length"
28
+ end
29
+
30
+ parse_catchall()
31
+
32
+ @opts[:first] = @opts[:first].to_i
33
+ @opts[:indat] ||= @stdin.read()
34
+ end
35
+
36
+
37
+ def go(*args)
38
+ super(*args)
39
+ @stdout << @opts[:indat][ @opts[:first] .. @opts[:last] ]
40
+ end
41
+
42
+ end
43
+
@@ -0,0 +1,116 @@
1
+ require 'rbkb/cli'
2
+ require 'rbkb/plug'
3
+ require 'eventmachine'
4
+
5
+
6
+ # This is an implementation of the original blackbag "telson" around
7
+ # ruby and eventmachine.
8
+ #
9
+ # Telson can do the following things with minimum fuss:
10
+ # - Run as a server or client using UDP or TCP
11
+ # - Debugging network protocols
12
+ # - Observe client/server behaviors using different messages at
13
+ # various phases of a conversation.
14
+ #
15
+ class Rbkb::Cli::Telson < Rbkb::Cli::Executable
16
+
17
+ def initialize(*args)
18
+ super(*args)
19
+ @b_addr = Plug::Blit::DEFAULT_IPADDR
20
+ @b_port = Plug::Blit::DEFAULT_PORT
21
+ @srced = @persist = false
22
+ @s_addr = "0.0.0.0"
23
+ @s_port = 0
24
+ @proto = :TCP
25
+
26
+ # XXX TODO Plug::UI obviously need fixing. It shouldn't be a module
27
+ # with constants for configuration
28
+ Plug::UI::LOGCFG[:verbose] = true
29
+ Plug::UI::LOGCFG[:dump] = :hex
30
+ Plug::UI::LOGCFG[:out] = @stderr
31
+ end
32
+
33
+
34
+ def make_parser()
35
+ arg = super()
36
+ arg.banner += " host:port"
37
+
38
+ arg.on("-u", "--udp", "UDP mode") do
39
+ @proto=:UDP
40
+ end
41
+
42
+ arg.on("-b", "--blit=ADDR:PORT", "Where to listen for blit") do |b|
43
+ unless m=/^(?:([\w\.]+):)?(\d+)$/.match(b)
44
+ bail("Invalid blit address/port")
45
+ end
46
+ @b_port = m[2].to_i
47
+ @b_addr = m[1] if m[1]
48
+ end
49
+
50
+ arg.on("-o", "--output=FILE", "Output to file instead of screen") do |f|
51
+ Plug::UI::LOGCFG[:out] = File.open(f, "w") # XXX
52
+ end
53
+
54
+ arg.on("-q", "--quiet", "Turn off verbose logging") do
55
+ Plug::UI::LOGCFG[:verbose] = false # XXX
56
+ end
57
+
58
+ arg.on("-r", "--reconnect", "Attempt to reconnect endlessly.") do
59
+ @persist=true
60
+ end
61
+
62
+ arg.on("-s", "--source=(ADDR:?)PORT", "Bind on port (and addr?)") do |p|
63
+ if m=/^(?:([\w\.]+):)?(\d+)$/.match(p)
64
+ @s_addr = $1 if $1
65
+ @s_port = $2.to_i
66
+ @srced = true
67
+ else
68
+ bail("Invalid listen argument: #{p.inspect}")
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ def parse(*args)
75
+ super(*args)
76
+
77
+ # Get target argument
78
+ unless (m = /^([\w\.]+):(\d+)$/.match(@argv.shift)) and @argv.shift.nil?
79
+ bail "Invalid target #{arg}"
80
+ end
81
+
82
+ @t_addr = m[1]
83
+ @t_port = m[2].to_i
84
+ end
85
+
86
+
87
+ def go(*args)
88
+ super(*args)
89
+
90
+ loop do
91
+ EventMachine.run {
92
+ if @proto == :TCP
93
+ bail("Sorry: --source only works with UDP.") if @srced
94
+
95
+ c=EventMachine.connect(@t_addr, @t_port, Plug::Telson, @proto)
96
+
97
+ elsif @proto == :UDP
98
+ c=EventMachine.open_datagram_socket(
99
+ @s_addr, @s_port, Plug::Telson, @proto
100
+ )
101
+ c.peers.add_peer_manually(@t_addr, @t_port)
102
+
103
+ ### someday maybe raw or others?
104
+ else
105
+ raise "bad protocol"
106
+ end
107
+
108
+ EventMachine.start_server(@b_addr, @b_port, Plug::Blit, :TCP, c)
109
+ Plug::UI::verbose("** BLITSRV-#{@b_addr}:#{@b_port}(TCP) Started") # XXX
110
+ }
111
+ break unless @persist
112
+ Plug::UI::verbose("** RECONNECTING") # XXX
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbkb/cli'
4
+
5
+ # unhexify converts a string of hex bytes back to raw data. Input can be
6
+ # supplied via stdin, a hex-string argument, or a file containing hex (use -f).
7
+ class Rbkb::Cli::Unhexify < Rbkb::Cli::Executable
8
+ def make_parser
9
+ super()
10
+ add_std_file_opt(:indat)
11
+ arg = @oparse
12
+
13
+ #----------------------------------------------------------------------
14
+ # Add local options
15
+ arg.banner += " <data | blank for stdin>"
16
+
17
+ arg.on("-d", "--delim DELIMITER",
18
+ "DELIMITER regex between hex chunks") do |d|
19
+ @opts[:delim] = Regexp.new(d.gsub('\\\\', '\\'))
20
+ end
21
+ end
22
+
23
+ def parse(*args)
24
+ super(*args)
25
+
26
+ # default string arg
27
+ if @opts[:indat].nil? and a=@argv.shift
28
+ @opts[:indat] = a.dup
29
+ end
30
+
31
+ # catchall
32
+ bail_args @argv.join(' ') if ARGV.length != 0
33
+ end
34
+
35
+ def go(*args)
36
+ super(*args)
37
+
38
+ # Default to standard input
39
+ @opts[:indat] ||= @stdin.read()
40
+
41
+ @opts[:indat].delete!("\r\n")
42
+ @opts[:delim] ||= /\s*/
43
+
44
+ unless out = @opts[:indat].unhexify(@opts[:delim])
45
+ bail "Error: Failed parsing as hex"
46
+ end
47
+
48
+ @stdout << out
49
+
50
+ self.exit(0)
51
+ end
52
+ end
53
+
@@ -0,0 +1,31 @@
1
+ require 'rbkb/cli'
2
+
3
+ # urldec converts a url percent-encoded string back to its raw form.
4
+ # Input can be supplied via stdin, a string argument, or a file (with -f).
5
+ # (url percent-encoding is just fancy hex encoding)
6
+ class Rbkb::Cli::Urldec < Rbkb::Cli::Executable
7
+ def make_parser()
8
+ super()
9
+ add_std_file_opt(:indat)
10
+ arg = @oparse
11
+ arg.banner += " <data | blank for stdin>"
12
+
13
+ arg.on("-p", "--[no-]plus", "Convert '+' to space (default: true)") do |p|
14
+ @opts[:noplus] = (not p)
15
+ end
16
+ end
17
+
18
+ def parse(*args)
19
+ super(*args)
20
+ parse_string_argument(:indat)
21
+ parse_catchall()
22
+ end
23
+
24
+ def go(*args)
25
+ super(*args)
26
+ # Default to standard input
27
+ @opts[:indat] ||= @stdin.read()
28
+ @stdout << @opts[:indat].urldec(:noplus => @opts[:noplus])
29
+ end
30
+ end
31
+
@@ -0,0 +1,31 @@
1
+ require 'rbkb/cli'
2
+
3
+ # urlenc converts a string or raw data to a url percent-encoded string
4
+ # Input can be supplied via stdin, a string argument, or a file (with -f).
5
+ # (url percent-encoding is just fancy hex encoding)
6
+ class Rbkb::Cli::Urlenc < Rbkb::Cli::Executable
7
+ def make_parser()
8
+ super()
9
+ add_std_file_opt(:indat)
10
+ arg = @oparse
11
+ arg.banner += " <data | blank for stdin>"
12
+
13
+ arg.on("-p", "--[no-]plus",
14
+ "Convert spaces to '+' (default: false)") do |p|
15
+ @opts[:plus] = p
16
+ end
17
+ end
18
+
19
+ def parse(*args)
20
+ super(*args)
21
+ parse_string_argument(:indat)
22
+ parse_catchall()
23
+ end
24
+
25
+ def go(*args)
26
+ super(*args)
27
+ # Default to standard input
28
+ @opts[:indat] ||= @stdin.read()
29
+ @stdout << @opts[:indat].urlenc(:plus => @opts[:plus]) + "\n"
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rbkb/cli'
3
+
4
+ # Repeating string xor. Takes input from a string, stdin, or a file (-f).
5
+ class Rbkb::Cli::Xor < Rbkb::Cli::Executable
6
+ def make_parser()
7
+ super()
8
+ add_std_file_opt(:indat)
9
+ arg = @oparse
10
+ arg.banner += " -k|-s <key> <data | stdin>"
11
+
12
+ arg.separator " Key options (you must specify one of the following):"
13
+ arg.on("-s", "--strkey STRING", "xor against STRING") do |s|
14
+ bail "only one key option can be specified with -s or -x" if @opts[:key]
15
+ @opts[:key] = s
16
+ end
17
+
18
+ arg.on("-x", "--hexkey HEXSTR", "xor against binary HEXSTR") do |x|
19
+ bail "only one key option can be specified with -s or -x" if @opts[:key]
20
+ x.sub!(/^0[xX]/, '')
21
+ bail "Unable to parse hex string" unless @opts[:key] = x.unhexify
22
+ end
23
+ return arg
24
+ end
25
+
26
+ def parse(*args)
27
+ super(*args)
28
+ bail("You must specify a key with -s or -x\n#{@oparse}") unless @opts[:key]
29
+ parse_string_argument(:indat)
30
+ parse_catchall()
31
+ end
32
+
33
+ def go(*args)
34
+ super(*args)
35
+ @opts[:indat] ||= @stdin.read
36
+ @stdout << @opts[:indat].xor(@opts[:key])
37
+ self.exit(0)
38
+ end
39
+ end
40
+
data/lib/rbkb/cli.rb ADDED
@@ -0,0 +1,212 @@
1
+ require 'rbkb'
2
+ require 'optparse'
3
+
4
+ module Rbkb::Cli
5
+ # Rbkb::Cli::Executable is an abstract class for creating command line
6
+ # executables using the Ruby Black Bag framework.
7
+ class Executable
8
+
9
+ def self.run(param={})
10
+ new(param).go
11
+ end
12
+
13
+ attr_accessor :stdout, :stderr, :stdin, :argv, :opts, :oparse
14
+
15
+ # Instantiates a new Executable object.
16
+ #
17
+ # The 'param' argument is a named value hash. The following keys are
18
+ # significant:
19
+ #
20
+ # :argv - An array of cli arguments (default ARGV)
21
+ # :opts - executable/function options for use when running 'go'
22
+ # :stdout, - IO redirection (mostly for unit tests)
23
+ # :stderr,
24
+ # :stdin
25
+ #
26
+ #
27
+ # The above keys are deleted from the 'param' hash and stored as instance
28
+ # variables with attr_accessors. All other parameters are ignored.
29
+ def initialize(param={})
30
+ @argv ||= param.delete(:argv) || ARGV
31
+ @stdout ||= param.delete(:stdout) || STDOUT
32
+ @stderr ||= param.delete(:stderr) || STDERR
33
+ @stdin ||= param.delete(:stdin) || STDIN
34
+ @opts ||= param.delete(:opts) || {}
35
+ make_parser()
36
+ yield self if block_given?
37
+ end
38
+
39
+
40
+ # Wrapper for Kernel.exit() so we can unit test cli tools
41
+ def exit(ret)
42
+ if defined? Rbkb::Cli::TESTING
43
+ raise("Exited with return code: #{ret}") if ret != 0
44
+ else
45
+ Kernel.exit(ret)
46
+ end
47
+ end
48
+
49
+
50
+ # This method exits with a message on stderr
51
+ def bail(msg)
52
+ @stderr.puts msg if msg
53
+ self.exit(1)
54
+ end
55
+
56
+
57
+ # This method wraps a 'bail' with a basic argument error mesage and hint
58
+ # for the '-h or --help' flag
59
+ # The 'arg_err' parameter is a string with the erroneous arguments
60
+ def bail_args(arg_err)
61
+ bail "Error: bad arguments - #{arg_err}\n Hint: Use -h or --help"
62
+ end
63
+
64
+
65
+ # Prepares an OptionsParser object with blackbag standard options
66
+ # This is called from within initialize() and should be overridden in
67
+ # inherited classes to add additional OptionParser-based parsers.
68
+ #
69
+ # See parse for actual parsing.
70
+ def make_parser
71
+ @oparse ||= OptionParser.new
72
+ @oparse.banner = "Usage: #{File.basename $0} [options]"
73
+
74
+ @oparse.on("-h", "--help", "Show this message") do
75
+ bail(@oparse)
76
+ end
77
+
78
+ @oparse.on("-v", "--version", "Show version and exit") do
79
+ bail("Ruby BlackBag version #{Rbkb::VERSION}")
80
+ end
81
+
82
+ return @oparse
83
+ end
84
+
85
+
86
+ # Abstract argument parser. Override this method with super() from
87
+ # inherited executables. The base method just calls OptionParser.parse!
88
+ # on the internal @oparse object.
89
+ def parse
90
+ # parse flag arguments
91
+ @oparse.parse!(@argv) rescue(bail_args($!))
92
+ @parsed=true
93
+
94
+ # the overriding class may implement additional arguments from here
95
+ end
96
+
97
+
98
+ # Abstract 'runner'. Override this method with super() from inherited
99
+ # executables. The base method just slurps in an optional argv and
100
+ # runs 'parse' if it hasn't already
101
+ def go(argv=nil)
102
+ if argv
103
+ @argv = argv
104
+ end
105
+
106
+ parse
107
+
108
+ # the overriding class implements actual functionality beyond here
109
+ end
110
+
111
+
112
+ private
113
+
114
+ # Wraps a file read with a standard bail error message
115
+ def do_file_read(f)
116
+ File.read(f) rescue(bail "File Read Error: #{$!}")
117
+ end
118
+
119
+
120
+ # Implements a basic input file argument. File reading is handled
121
+ # by do_file_read().
122
+ #
123
+ # Takes one argument, which is the @opts hash keyname to store
124
+ # the file data into.
125
+ # (Used commonly throughout several executables)
126
+ def add_std_file_opt(inkey)
127
+ @oparse.on("-f", "--file FILENAME", "Input from FILENAME") do |f|
128
+ @opts[inkey] = do_file_read(f)
129
+ end
130
+ return @oparse
131
+ end
132
+
133
+
134
+ # Implements numeric and hex range options via '-r' and '-x'
135
+ #
136
+ # Takes two arguments which are the @opts hash key names for
137
+ # first and last parameters.
138
+ #
139
+ # (Used commonly throughout several executables)
140
+ def add_range_opts(fkey, lkey)
141
+ @oparse.on("-r", "--range=START[:END]",
142
+ "Start and optional end range") do |r|
143
+
144
+ raise "-x and -r are mutually exclusive" if @parser_got_range
145
+ @parser_got_range=true
146
+
147
+ unless m=/^(-?[0-9]+)(?::(-?[0-9]+))?$/.match(r)
148
+ raise "invalid range #{r.inspect}"
149
+ end
150
+
151
+ @opts[fkey] = $1.to_i
152
+ @opts[lkey] = $2.to_i if $2
153
+ end
154
+
155
+ @oparse.on("-x", "--hexrange=START[:END]",
156
+ "Start and optional end range in hex") do |r|
157
+
158
+ raise "-x and -r are mutually exclusive" if @parser_got_range
159
+ @parser_got_range=true
160
+
161
+ unless m=/^(-?[0-9a-f]+)(?::(-?[0-9a-f]+))?$/i.match(r)
162
+ raise "invalid range #{r.inspect}"
163
+ end
164
+
165
+ @opts[fkey] =
166
+ if ($1[0,1] == '-')
167
+ ($1[1..-1]).hex_to_num * -1
168
+ else
169
+ $1.hex_to_num
170
+ end
171
+
172
+ if $2
173
+ @opts[lkey] =
174
+ if($2[0,1] == '-')
175
+ $2[1..-1].hex_to_num * -1
176
+ else
177
+ $2.hex_to_num
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+
184
+ # Conditionally parses a string argument. Uses 'key' to first check for
185
+ # then store it in @opts hash if it is not yet there.
186
+ # (Used commonly throughout several executables)
187
+ def parse_string_argument(key)
188
+ if @opts[key].nil? and s=@argv.shift
189
+ @opts[key] = s.dup
190
+ end
191
+ end
192
+
193
+
194
+ # Conditionally parses a file argument. Uses 'key' to first check for
195
+ # then store it in @opts hash if it is not yet there.
196
+ # (Used commonly throughout several executables)
197
+ def parse_file_argument(key)
198
+ if @opts[key].nil? and f=@argv.shift
199
+ @opts[key] = do_file_read(f)
200
+ end
201
+ end
202
+
203
+ # For use at the end of a parser - calls bail_args with remaining
204
+ # arguments if there are extra arguments.
205
+ # (Used commonly throughout several executables)
206
+ def parse_catchall
207
+ bail_args(@argv.join(' ')) if(@argv.length != 0)
208
+ end
209
+
210
+ end
211
+ end
212
+