emonti-rbkb 0.6.2.1 → 0.6.6

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 (78) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +10 -7
  3. data/Rakefile +47 -0
  4. data/bin/feed +5 -0
  5. data/bin/plugsrv +3 -3
  6. data/cli_usage.rdoc +44 -9
  7. data/doctor-bag.jpg +0 -0
  8. data/lib/rbkb.rb +47 -2
  9. data/lib/rbkb/cli.rb +8 -6
  10. data/lib/rbkb/cli/b64.rb +5 -0
  11. data/lib/rbkb/cli/bgrep.rb +14 -9
  12. data/lib/rbkb/cli/chars.rb +2 -1
  13. data/lib/rbkb/cli/crc32.rb +4 -1
  14. data/lib/rbkb/cli/d64.rb +3 -0
  15. data/lib/rbkb/cli/dedump.rb +5 -3
  16. data/lib/rbkb/cli/feed.rb +223 -0
  17. data/lib/rbkb/cli/hexify.rb +3 -3
  18. data/lib/rbkb/cli/len.rb +12 -9
  19. data/lib/rbkb/cli/rstrings.rb +13 -10
  20. data/lib/rbkb/cli/slice.rb +1 -0
  21. data/lib/rbkb/cli/telson.rb +21 -57
  22. data/lib/rbkb/cli/unhexify.rb +2 -6
  23. data/lib/rbkb/cli/urldec.rb +1 -0
  24. data/lib/rbkb/cli/urlenc.rb +1 -0
  25. data/lib/rbkb/extends.rb +41 -6
  26. data/lib/rbkb/http.rb +20 -0
  27. data/lib/rbkb/http/base.rb +172 -0
  28. data/lib/rbkb/http/body.rb +214 -0
  29. data/lib/rbkb/http/common.rb +74 -0
  30. data/lib/rbkb/http/headers.rb +356 -0
  31. data/lib/rbkb/http/parameters.rb +101 -0
  32. data/lib/rbkb/http/request.rb +58 -0
  33. data/lib/rbkb/http/response.rb +86 -0
  34. data/lib/rbkb/plug.rb +3 -3
  35. data/lib/rbkb/plug/cli.rb +83 -0
  36. data/lib/rbkb/plug/feed_import.rb +74 -0
  37. data/lib/rbkb/plug/plug.rb +36 -19
  38. data/lib/rbkb/plug/unix_domain.rb +75 -0
  39. data/rbkb.gemspec +38 -0
  40. data/spec/rbkb_spec.rb +7 -0
  41. data/spec/spec_helper.rb +16 -0
  42. data/tasks/ann.rake +80 -0
  43. data/tasks/bones.rake +20 -0
  44. data/tasks/gem.rake +201 -0
  45. data/tasks/git.rake +40 -0
  46. data/tasks/notes.rake +27 -0
  47. data/tasks/post_load.rake +34 -0
  48. data/tasks/rdoc.rake +51 -0
  49. data/tasks/rubyforge.rake +55 -0
  50. data/tasks/setup.rb +292 -0
  51. data/tasks/spec.rake +54 -0
  52. data/tasks/svn.rake +47 -0
  53. data/tasks/test.rake +40 -0
  54. data/test/test_cli_b64.rb +35 -0
  55. data/test/test_cli_bgrep.rb +137 -0
  56. data/test/test_cli_blit.rb +11 -0
  57. data/test/test_cli_chars.rb +21 -0
  58. data/test/test_cli_crc32.rb +108 -0
  59. data/test/test_cli_d64.rb +22 -0
  60. data/test/test_cli_dedump.rb +118 -0
  61. data/test/test_cli_feed.rb +11 -0
  62. data/test/test_cli_helper.rb +96 -0
  63. data/test/test_cli_hexify.rb +63 -0
  64. data/test/test_cli_len.rb +96 -0
  65. data/test/test_cli_rstrings.rb +15 -0
  66. data/test/test_cli_slice.rb +73 -0
  67. data/test/test_cli_telson.rb +11 -0
  68. data/test/test_cli_unhexify.rb +43 -0
  69. data/test/test_cli_urldec.rb +50 -0
  70. data/test/test_cli_urlenc.rb +44 -0
  71. data/test/test_cli_xor.rb +71 -0
  72. data/test/test_helper.rb +5 -0
  73. data/test/test_http.rb +27 -0
  74. data/test/test_http_helper.rb +60 -0
  75. data/test/test_http_request.rb +136 -0
  76. data/test/test_http_response.rb +222 -0
  77. data/test/test_rbkb.rb +19 -0
  78. metadata +127 -21
@@ -0,0 +1,58 @@
1
+ module Rbkb::Http
2
+
3
+ # A Request encapsulates all the entities in a HTTP request message
4
+ # including the action header, general headers, and body.
5
+ class Request < Base
6
+ attr_accessor :action
7
+
8
+ alias first_entity action
9
+ alias first_entity= action=
10
+
11
+ def request_parameters
12
+ @action.parameters
13
+ end
14
+
15
+ # Returns a new Headers object extended as RequestHeaders. This is the
16
+ # default object which will be used when composing fresh Request header
17
+ # entities.
18
+ def default_headers_obj(*args)
19
+ Headers.new(*args).extend(RequestHeaders)
20
+ end
21
+
22
+ # Returns a new BoundBody object. This is the default object which will
23
+ # be used when composing fresh Request body entities.
24
+ def default_body_obj(*args)
25
+ Body.new(*args)
26
+ end
27
+
28
+ # Returns a raw HTTP request for this instance. The instance must have
29
+ # an action element defined at the bare minimum.
30
+ def to_raw(tmp_body=@body)
31
+ raise "this request has no action entity" unless first_entity()
32
+ self.headers ||= default_headers_obj()
33
+ self.body ||= default_body_obj()
34
+
35
+ if len=@opts[:static_length]
36
+ @body = Body.new(@body, @body.opts) {|x| x.base = self}
37
+ @headers.set_header("Content-Length", len.to_i)
38
+ elsif @opts[:ignore_content_length]
39
+ @headers.delete_header("Content-Length")
40
+ end
41
+
42
+ bstr = tmp_body.to_raw
43
+ hdrs = (@headers).to_raw_array.unshift(first_entity.to_raw)
44
+ return "#{hdrs.join("\r\n")}\r\n\r\n#{bstr}"
45
+ end
46
+
47
+
48
+ # Parses a raw HTTP request and captures data into the current instance.
49
+ def capture(str)
50
+ raise "arg 0 must be a string" unless String === str
51
+ hstr, bstr = str.split(/\s*\r?\n\r?\n/, 2)
52
+ capture_headers(hstr)
53
+ self.body = content_length ? BoundBody.new : Body.new
54
+ capture_body(bstr)
55
+ return self
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,86 @@
1
+
2
+ module Rbkb::Http
3
+ # A Response encapsulates all the entities in a HTTP response,
4
+ # including the status header, general headers, and body.
5
+ class Response < Base
6
+ attr_accessor :status
7
+
8
+ alias first_entity status
9
+ alias first_entity= status=
10
+
11
+ # Returns a raw HTTP response for this instance. Must have a status
12
+ # element defined at a bare minimum.
13
+ def to_raw(raw_body=nil)
14
+ raise "this response has no status" unless first_entity()
15
+ self.headers ||= default_headers_obj()
16
+ self.body = raw_body if raw_body
17
+
18
+ if do_chunked_encoding?(@headers)
19
+ unless @body.is_a? ChunkedBody
20
+ @body = ChunkedBody.new(@body, @body.opts)
21
+ end
22
+ @headers.delete_header("Content-Length")
23
+ elsif not opts[:ignore_content_length]
24
+ unless @body.is_a? BoundBody
25
+ @body = BoundBody.new(@body, @body.opts)
26
+ end
27
+ @headers.delete_header("Transfer-Encoding")
28
+ else
29
+ @body = Body.new(@body, @body.opts)
30
+ end
31
+ @body.base = self
32
+
33
+ yield(self) if block_given?
34
+
35
+ bstr = @body.to_raw
36
+ hdrs = @headers.to_raw_array.unshift(self.first_entity.to_raw)
37
+ return "#{hdrs.join("\r\n")}\r\n\r\n#{bstr}"
38
+ end
39
+
40
+ # Parses a raw HTTP response and captures data into the current instance.
41
+ def capture(str)
42
+ raise "arg 0 must be a string" unless String === str
43
+ hstr, bstr = str.split(/\s*\r?\n\r?\n/, 2)
44
+
45
+ capture_headers(hstr)
46
+
47
+ yield(self, bstr) if block_given?
48
+
49
+ unless @body and @body.capture_complete?
50
+ @body =
51
+ if do_chunked_encoding?
52
+ ChunkedBody.new {|b| b.base = self }
53
+ elsif content_length()
54
+ BoundBody.new {|b| b.base = self }
55
+ else
56
+ Body.new {|b| b.base = self }
57
+ end
58
+ end
59
+
60
+ capture_body(bstr)
61
+
62
+ return self
63
+ end
64
+
65
+ # Indicates whether to use chunked encoding based on presence of
66
+ # the "Transfer-Encoding: chunked" header or the :ignore_chunked_encoding
67
+ # opts parameter.
68
+ def do_chunked_encoding?(hdrs=@headers)
69
+ ( (not @opts[:ignore_chunked_encoding]) and
70
+ (hdrs.get_header_value("Transfer-Encoding").first =~ /(?:^|\W)chunked(?:\W|$)/) )
71
+ end
72
+
73
+ # Returns a new Headers object extended as ResponseHeaders. This is the
74
+ # default object which will be used when composing fresh Response header
75
+ # entities.
76
+ def default_headers_obj(*args)
77
+ Headers.new(*args).extend(ResponseHeaders)
78
+ end
79
+
80
+ # Returns a new BoundBody object. This is the default object which will
81
+ # be used when composing fresh Response body entities.
82
+ def default_body_obj(*args)
83
+ BoundBody.new(*args)
84
+ end
85
+ end
86
+ end
data/lib/rbkb/plug.rb CHANGED
@@ -2,8 +2,8 @@
2
2
  # See README.rdoc for license information
3
3
  #
4
4
 
5
- require "rbkb.rb"
5
+ require "rbkb"
6
6
 
7
- require "rbkb/plug/plug.rb"
8
- require "rbkb/plug/blit.rb"
7
+ require "rbkb/plug/plug"
8
+ require "rbkb/plug/blit"
9
9
 
@@ -0,0 +1,83 @@
1
+ require 'rbkb/cli'
2
+ require 'rbkb/plug'
3
+ require 'eventmachine'
4
+
5
+
6
+ # Copyright 2009 emonti at matasano.com
7
+ # See README.rdoc for license information
8
+ #
9
+ module Rbkb::Cli
10
+
11
+ # Rbkb::Cli::Executable is an abstract class for creating command line
12
+ # executables using the Ruby Black Bag framework.
13
+ class PlugCli < Executable
14
+ RX_HOST_AND_PORT = /^([\w\._-]+):(\d+)$/
15
+ RX_PORT_OPT_ADDR = /^(?:([\w\._-]+):)?(\d+)$/
16
+
17
+ attr_accessor :blit_addr, :blit_port, :blit_proto,
18
+ :local_addr, :local_port, :transport,
19
+ :target_addr, :target_port, :plug_opts
20
+
21
+ def initialize(*args)
22
+ super(*args) do |this|
23
+ this.blit_addr ||= Plug::Blit::DEFAULT_IPADDR
24
+ this.blit_port ||= Plug::Blit::DEFAULT_PORT
25
+ this.transport ||= :TCP
26
+ this.plug_opts ||= {}
27
+ yield this if block_given?
28
+ end
29
+
30
+ # TODO Plug::UI obviously need fixing.
31
+ # TODO It shouldn't be driven by constants for configuration
32
+ Plug::UI::LOGCFG[:verbose] = true
33
+ Plug::UI::LOGCFG[:dump] = :hex
34
+ Plug::UI::LOGCFG[:out] = @stderr
35
+ end
36
+
37
+ def make_parser()
38
+ arg = super()
39
+ arg.banner << " host:port"
40
+
41
+ arg.on("-o", "--output=FILE", "Output to file") do |o|
42
+ Plug::UI::LOGCFG[:out] = File.open(o, "w") # XXX
43
+ end
44
+
45
+ arg.on("-q", "--quiet", "Turn off verbose logging") do
46
+ Plug::UI::LOGCFG[:verbose] = false # XXX
47
+ end
48
+
49
+ arg.on("-d", "--dump-format=hex/raw",
50
+ "Output conversations in hexdump or raw") do |d|
51
+ if m=/^(hex|raw)$/i.match(d)
52
+ Plug::UI::LOGCFG[:dump] = m[1].downcase.to_sym # XXX
53
+ else
54
+ bail "Invalid dump format: #{d.inspect}"
55
+ end
56
+ end
57
+
58
+ arg.on("-b", "--blit=ADDR:PORT", "Where to listen for blit") do |b|
59
+ unless m=RX_PORT_OPT_ADDR.match(b)
60
+ bail("Invalid blit address/port")
61
+ end
62
+ @blit_port = m[2].to_i
63
+ @blit_addr = m[1] if m[1]
64
+ end
65
+
66
+ arg.on("-u", "--udp", "UDP mode") { @transport=:UDP }
67
+
68
+ arg.on("-S", "--start-tls", "Initiate TLS") {|s| @plug_opts[:tls]=true }
69
+
70
+ return arg
71
+ end
72
+
73
+ def parse_target_argument()
74
+ unless (m = RX_HOST_AND_PORT.match(tgt=@argv.shift))
75
+ bail "Invalid target: #{tgt}\n Hint: use -h"
76
+ end
77
+ @target_addr = m[1]
78
+ @target_port = m[2].to_i
79
+ return m
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,74 @@
1
+ # Copyright 2009 emonti at matasano.com
2
+ # See README.rdoc for license information
3
+ #
4
+
5
+ require 'yaml'
6
+ require 'rbkb'
7
+
8
+ module FeedImport
9
+
10
+ ## TODO switch to pcaprub or some other up to date pcap lib. make it a dep.
11
+ begin
12
+ ## This requires the 'ruby-pcap' library from:
13
+ ## http://raa.ruby-lang.org/project/pcap/
14
+ ## ... which is old and krufty...
15
+ $VERBOSE=nil
16
+ require 'pcaplet'
17
+ $VERBOSE=false
18
+
19
+ # Imports an array from pcap
20
+ def import_pcap(file, filter=nil)
21
+ ret = Array.new
22
+ pcap = Pcap::Capture.open_offline(file)
23
+ pcap.setfilter filter if filter
24
+ pcap.each_packet do |pkt|
25
+ if ( (pkt.udp? and dat=pkt.udp_data) or
26
+ (pkt.tcp? and dat=pkt.tcp_data and not dat.empty?)
27
+ )
28
+ ret << dat
29
+ end
30
+ end
31
+ return ret
32
+ end
33
+ rescue LoadError
34
+ def import_pcap(*args)
35
+ raise "you must install ruby-pcap to use this feature"
36
+ end
37
+ end
38
+
39
+ module_function :import_pcap
40
+
41
+
42
+ # Imports an array from yaml
43
+ def import_yaml(file)
44
+ unless ( ret = YAML.load_file(file) ).kind_of? Array
45
+ raise "#{file.inspect} did not provide an array"
46
+ end
47
+ return ret
48
+ end
49
+ module_function :import_yaml
50
+
51
+
52
+ # Imports from hexdumps separated by "%" and merged by ','
53
+ def import_dump(file)
54
+ ret = []
55
+ dat = File.read(file)
56
+ dat.strip.split(/^%$/).each do |msg|
57
+ ret << ""
58
+ msg.strip.split(/^,$/).each do |chunk|
59
+ ret[-1] << chunk.strip.dehexdump
60
+ end
61
+ end
62
+ return ret
63
+ end
64
+ module_function :import_dump
65
+
66
+ # Imports raw messages in files by a glob pattern (i.e. /tmp/foo/msgs.*)
67
+ # Manage filenames so that they're in the right order on import.
68
+ # See Dir.glob for valid globbing patterns.
69
+ def import_rawfiles(glob_pat)
70
+ Dir.glob(glob_pat).map { |f| File.read(f) }
71
+ end
72
+ module_function :import_rawfiles
73
+ end
74
+
@@ -21,21 +21,37 @@ module Plug
21
21
 
22
22
  def self.dump(from, to, dat)
23
23
  if dump=LOGCFG[:dump]
24
- LOGCFG[:out].puts "%% #{from} SAYS TO #{to} LEN=#{dat.size}",
25
- (dump == :hex)? dat.hexdump(:out => StringIO.new) : dat,
26
- "%%"
24
+ LOGCFG[:out].puts "%% #{from} SAYS TO #{to} LEN=#{dat.size}" if LOGCFG[:verbose]
25
+ case dump
26
+ when :hex
27
+ dat.hexdump(:out => LOGCFG[:out])
28
+ when :raw
29
+ LOGCFG[:out].puts dat
30
+ else
31
+ LOGCFG[:out].puts dat
32
+ end
33
+ LOGCFG[:out].puts "%%" if LOGCFG[:verbose]
27
34
  end
28
35
  end
29
36
  end
30
37
 
31
38
 
32
39
  module Base
33
- attr_accessor :peers, :transport, :kind
40
+ attr_accessor :peers, :transport, :kind, :tls, :tls_opts, :no_stop_on_unbind
34
41
 
35
- def initialize(transport)
42
+ def initialize(transport, opts={})
36
43
  # raise "Invalid transport #{transport.inspect}" unless (:UDP, :TCP).include?(transport)
37
44
  @transport = transport
38
45
  @peers = PeerList.new(self)
46
+
47
+ opts.each_pair do |k,v|
48
+ accessor = k.to_s + "="
49
+ if self.respond_to?(accessor)
50
+ self.send(accessor, v)
51
+ else
52
+ raise "Bad attribute: #{k}"
53
+ end
54
+ end
39
55
  end
40
56
 
41
57
  def name
@@ -81,6 +97,9 @@ module Plug
81
97
  UI.verbose "** #{name} Started"
82
98
  if @kind==:server and peer=plug_peer
83
99
  UI.log "** #{name} CONNECTED TO #{peer.name}"
100
+ if tls
101
+ start_tls(tls_opts || {})
102
+ end
84
103
  end
85
104
  end
86
105
 
@@ -94,12 +113,18 @@ module Plug
94
113
  def connection_completed
95
114
  peer = plug_peer
96
115
  UI.log "** #{name} CONNECTED TO #{peer.name}"
116
+ if tls
117
+ start_tls(tls_opts || {})
118
+ end
97
119
  return peer
98
120
  end
99
121
 
100
122
  def unbind
101
123
  UI.log "** Connection " + ((@peers.empty?)? "refused." : "closed.")
102
- EM.stop
124
+ unless @no_stop_on_unbind
125
+ UI.log "STOPPING!!"
126
+ EM.stop
127
+ end
103
128
  end
104
129
  end
105
130
 
@@ -137,21 +162,13 @@ module Plug
137
162
  attr_accessor :pos, :feed, :step, :close_at_end, :go_first,
138
163
  :squelch_exhausted
139
164
 
140
- def initialize(transport, opts={})
141
- super(transport)
165
+ def initialize(*args)
166
+ super(*args)
142
167
 
143
- @pos = 0
144
- @feed = []
168
+ @pos ||= 0
169
+ @feed ||= []
145
170
 
146
- opts.each_pair do |k,v|
147
- accessor = k.to_s + "="
148
- if self.respond_to?(accessor)
149
- self.send(accessor, v)
150
- else
151
- raise "Bad attribute: #{k}"
152
- end
153
- end
154
- raise "feed must be enumerable" unless @feed.kind_of? Enumerable
171
+ raise "feed must be enumerable" unless Enumerable === @feed
155
172
  end
156
173
 
157
174
  def go
@@ -0,0 +1,75 @@
1
+ # Copyright 2009 emonti at matasano.com
2
+ # See README.rdoc for license information
3
+ #
4
+
5
+ # Experimental!!!
6
+
7
+ require 'eventmachine'
8
+ require 'rbkb/plug'
9
+ require 'rbkb'
10
+
11
+ module Plug
12
+ class PeerStub
13
+ attr_reader :owner
14
+
15
+ def initialize(owner)
16
+ @owner = owner
17
+ end
18
+
19
+ def [](junk)
20
+ @owner
21
+ end
22
+
23
+ def []=(junk)
24
+ [@owner]
25
+ end
26
+
27
+ def peers
28
+ nil
29
+ end
30
+ end
31
+
32
+ module UnixDomain
33
+ attr_accessor :mute, :peers
34
+
35
+ def initialize
36
+ @peers = PeerStub.new(self)
37
+ end
38
+
39
+ def name
40
+ "a domain socket"
41
+ end
42
+
43
+ def receive_data(dat)
44
+ puts "Got:", dat.hexdump
45
+ end
46
+
47
+ def say(dat, sender)
48
+ UI.dump(sender.name, self.name, dat)
49
+ send_data(dat)
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ if $0 == __FILE__
56
+ Plug::UI::LOGCFG[:verbose] = true
57
+
58
+ b_addr = Plug::Blit::DEFAULT_IPADDR
59
+ b_port = Plug::Blit::DEFAULT_PORT
60
+ unless (sock=ARGV.shift and ARGV.shift.nil?)
61
+ STDERR.puts "usage: #{File.basename $0} unix_socket"
62
+ exit 1
63
+ end
64
+
65
+
66
+ EventMachine.run {
67
+ s = EventMachine.connect_unix_domain(sock, Plug::UnixDomain)
68
+ Plug::UI::verbose("** UNIX-DOMAIN-#{sock.inspect} Started")
69
+
70
+ # connect a blit channel:
71
+ EventMachine.start_server(b_addr, b_port, Plug::Blit, :TCP, s)
72
+ Plug::UI::verbose("** BLITSRV-#{b_addr}:#{b_port}(TCP) Started")
73
+ }
74
+
75
+ end