emonti-rbkb 0.6.2.1 → 0.6.6

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