emonti-rbkb 0.6.1.3 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+