rbkb 0.6.10

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 (101) hide show
  1. data/History.txt +74 -0
  2. data/README.rdoc +149 -0
  3. data/Rakefile +47 -0
  4. data/bin/b64 +5 -0
  5. data/bin/bgrep +5 -0
  6. data/bin/blit +5 -0
  7. data/bin/c +5 -0
  8. data/bin/crc32 +5 -0
  9. data/bin/d64 +5 -0
  10. data/bin/dedump +5 -0
  11. data/bin/feed +5 -0
  12. data/bin/hexify +5 -0
  13. data/bin/len +5 -0
  14. data/bin/plugsrv +271 -0
  15. data/bin/rex +10 -0
  16. data/bin/rstrings +5 -0
  17. data/bin/slice +5 -0
  18. data/bin/telson +5 -0
  19. data/bin/unhexify +5 -0
  20. data/bin/urldec +5 -0
  21. data/bin/urlenc +5 -0
  22. data/bin/xor +5 -0
  23. data/cli_usage.rdoc +285 -0
  24. data/doctor-bag.jpg +0 -0
  25. data/lib/rbkb.rb +51 -0
  26. data/lib/rbkb/cli.rb +219 -0
  27. data/lib/rbkb/cli/b64.rb +35 -0
  28. data/lib/rbkb/cli/bgrep.rb +86 -0
  29. data/lib/rbkb/cli/blit.rb +89 -0
  30. data/lib/rbkb/cli/chars.rb +24 -0
  31. data/lib/rbkb/cli/crc32.rb +35 -0
  32. data/lib/rbkb/cli/d64.rb +28 -0
  33. data/lib/rbkb/cli/dedump.rb +52 -0
  34. data/lib/rbkb/cli/feed.rb +229 -0
  35. data/lib/rbkb/cli/hexify.rb +65 -0
  36. data/lib/rbkb/cli/len.rb +76 -0
  37. data/lib/rbkb/cli/rstrings.rb +108 -0
  38. data/lib/rbkb/cli/slice.rb +47 -0
  39. data/lib/rbkb/cli/telson.rb +87 -0
  40. data/lib/rbkb/cli/unhexify.rb +50 -0
  41. data/lib/rbkb/cli/urldec.rb +35 -0
  42. data/lib/rbkb/cli/urlenc.rb +35 -0
  43. data/lib/rbkb/cli/xor.rb +43 -0
  44. data/lib/rbkb/extends.rb +725 -0
  45. data/lib/rbkb/http.rb +21 -0
  46. data/lib/rbkb/http/base.rb +172 -0
  47. data/lib/rbkb/http/body.rb +214 -0
  48. data/lib/rbkb/http/common.rb +74 -0
  49. data/lib/rbkb/http/headers.rb +370 -0
  50. data/lib/rbkb/http/parameters.rb +104 -0
  51. data/lib/rbkb/http/request.rb +58 -0
  52. data/lib/rbkb/http/response.rb +86 -0
  53. data/lib/rbkb/plug.rb +9 -0
  54. data/lib/rbkb/plug/blit.rb +222 -0
  55. data/lib/rbkb/plug/cli.rb +83 -0
  56. data/lib/rbkb/plug/feed_import.rb +74 -0
  57. data/lib/rbkb/plug/peer.rb +67 -0
  58. data/lib/rbkb/plug/plug.rb +215 -0
  59. data/lib/rbkb/plug/proxy.rb +26 -0
  60. data/lib/rbkb/plug/unix_domain.rb +75 -0
  61. data/lib_usage.rdoc +176 -0
  62. data/rbkb.gemspec +38 -0
  63. data/spec/rbkb_spec.rb +7 -0
  64. data/spec/spec_helper.rb +16 -0
  65. data/tasks/ann.rake +80 -0
  66. data/tasks/bones.rake +20 -0
  67. data/tasks/gem.rake +201 -0
  68. data/tasks/git.rake +40 -0
  69. data/tasks/notes.rake +27 -0
  70. data/tasks/post_load.rake +34 -0
  71. data/tasks/rdoc.rake +51 -0
  72. data/tasks/rubyforge.rake +55 -0
  73. data/tasks/setup.rb +292 -0
  74. data/tasks/spec.rake +54 -0
  75. data/tasks/svn.rake +47 -0
  76. data/tasks/test.rake +40 -0
  77. data/test/test_cli_b64.rb +35 -0
  78. data/test/test_cli_bgrep.rb +137 -0
  79. data/test/test_cli_blit.rb +11 -0
  80. data/test/test_cli_chars.rb +21 -0
  81. data/test/test_cli_crc32.rb +108 -0
  82. data/test/test_cli_d64.rb +22 -0
  83. data/test/test_cli_dedump.rb +118 -0
  84. data/test/test_cli_feed.rb +11 -0
  85. data/test/test_cli_helper.rb +96 -0
  86. data/test/test_cli_hexify.rb +63 -0
  87. data/test/test_cli_len.rb +96 -0
  88. data/test/test_cli_rstrings.rb +15 -0
  89. data/test/test_cli_slice.rb +73 -0
  90. data/test/test_cli_telson.rb +11 -0
  91. data/test/test_cli_unhexify.rb +43 -0
  92. data/test/test_cli_urldec.rb +50 -0
  93. data/test/test_cli_urlenc.rb +44 -0
  94. data/test/test_cli_xor.rb +71 -0
  95. data/test/test_helper.rb +5 -0
  96. data/test/test_http.rb +27 -0
  97. data/test/test_http_helper.rb +60 -0
  98. data/test/test_http_request.rb +136 -0
  99. data/test/test_http_response.rb +222 -0
  100. data/test/test_rbkb.rb +19 -0
  101. metadata +238 -0
@@ -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").to_s =~ /(?:^|\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 ADDED
@@ -0,0 +1,9 @@
1
+ # Copyright 2009 emonti at matasano.com
2
+ # See README.rdoc for license information
3
+ #
4
+
5
+ require "rbkb"
6
+
7
+ require "rbkb/plug/plug"
8
+ require "rbkb/plug/blit"
9
+
@@ -0,0 +1,222 @@
1
+ # Copyright 2009 emonti at matasano.com
2
+ # See README.rdoc for license information
3
+ #
4
+ module Plug
5
+ module Blit
6
+ include Base
7
+
8
+ DEFAULT_IPADDR = "127.0.0.1"
9
+ DEFAULT_PORT = 25195
10
+ DEFAULT_PROTOCOL = :TCP
11
+
12
+ OPCODES = {
13
+ 0 => :squelch,
14
+ 1 => :unsquelch,
15
+ 2 => :delete,
16
+ 5 => :sendmsg,
17
+ 6 => :list_peers,
18
+
19
+ 0xfe => :clear,
20
+ 0xff => :kill,
21
+ }
22
+
23
+ attr_accessor :kind
24
+
25
+ def initialize(transport, slave)
26
+ super(transport)
27
+
28
+ @kind = :blitsrv
29
+ @slave = slave
30
+ @peers = slave.peers
31
+ initbuf
32
+ end
33
+
34
+ def post_init
35
+ # override so we don't get unneccessary "Start" message from Base
36
+ end
37
+
38
+ def unbind
39
+ # override so we don't get unneccessary "closed" message from Base
40
+ end
41
+
42
+
43
+ ### Blit protocol stuff
44
+ SIG = "BLT"
45
+
46
+ # (re)initializes the blit buffer
47
+ def initbuf
48
+ @buf = StringIO.new
49
+ end
50
+
51
+ def receive_data dat
52
+ return unless (@buf.write(dat) > SIG.size) or (@buf.pos > (SIG.size + 1))
53
+
54
+ @buf.rewind
55
+
56
+ return unless @buf.read(SIG.size) == SIG and
57
+ op = OPCODES[ @buf.read(1)[0] ]
58
+
59
+ initbuf if self.send(op)
60
+ end
61
+
62
+
63
+ def self.blit_header(op)
64
+ return nil unless opno = OPCODES.invert[op]
65
+ SIG + opno.chr
66
+ end
67
+
68
+ def mute
69
+ unless ( peerno=@buf.read(2) and peerno.size == 2 and
70
+ peer=@peers[peerno.dat_to_num(:big)] )
71
+
72
+ UI.log "** BLIT-ERROR(Malformed or missing peer for mute)"
73
+ return true
74
+ end
75
+ end
76
+
77
+ def self.make_mute(peerno)
78
+ self.blit_header(:squelch) +
79
+ peerno.to_bytes(:big, 2)
80
+ end
81
+
82
+ def unmute
83
+ unless ( peerno=@buf.read(2) and peerno.size == 2 and
84
+ peer=@peers[peerno.dat_to_num(:big)] )
85
+ UI.log "** BLIT-ERROR(Malformed or missing peer for unmute)"
86
+ return true
87
+ end
88
+ end
89
+
90
+ def self.make_squelch(peerno)
91
+ self.blit_header(:squelch) +
92
+ peerno.to_bytes(:big, 2)
93
+ end
94
+
95
+ def sendmsg
96
+ unless peerno=@buf.read(2) and peerno.size == 2 and
97
+ bufsiz=@buf.read(4) and bufsiz.size == 4
98
+ UI.log "** BLIT-ERROR(Malformed sendmsg)"
99
+ return true
100
+ end
101
+
102
+ peerno = peerno.dat_to_num(:big)
103
+ bufsiz = bufsiz.dat_to_num(:big)
104
+
105
+ if (rdat=@buf.read(bufsiz)).size == bufsiz
106
+ if peer=@peers[peerno]
107
+ peer.say(rdat, self)
108
+ return true
109
+ else
110
+ UI.log "** BLIT-ERROR(Invalid peer index #{peerno})"
111
+ return true
112
+ end
113
+ else
114
+ return nil
115
+ end
116
+ end
117
+
118
+ # Blit packed message format is (SUBJECT TO CHANGE):
119
+ # "BLT"
120
+ # char opcode
121
+ # uint16be idx = index of slave peer to send to
122
+ # uint32le size = length of data
123
+ # str data
124
+ def self.make_sendmsg(idx, dat)
125
+ self.blit_header(:sendmsg) +
126
+ idx.to_bytes(:big, 2) +
127
+ dat.size.to_bytes(:big, 4) +
128
+ dat
129
+ end
130
+
131
+ def kill
132
+ UI.log("** BLIT-KILL - Received shutdown command")
133
+ EM.stop
134
+ end
135
+
136
+ def self.make_kill(idx=nil)
137
+ self.blit_header(:kill)
138
+ end
139
+
140
+ def clear
141
+ @peers.each { |p| p.close }
142
+ @peers.replace []
143
+ end
144
+
145
+ def self.make_clear
146
+ self.blit_header(:clear)
147
+ end
148
+
149
+ def delete(peerno)
150
+ @peers.delete(peerno)
151
+ end
152
+
153
+ def self.make_delete(idx=0)
154
+ self.blit_header(:delete) +
155
+ idx.to_bytes(:big, 2)
156
+ end
157
+
158
+ def list_peers
159
+ UI.log("** BLIT-LISTPEERS - Received list peers command")
160
+
161
+ @peers.each_index {|i| UI.log "** #{i} - #{@peers[i].name}"}
162
+ UI.log("** BLIT-LISTPEERS-END - End of peer list")
163
+ end
164
+
165
+ def self.make_list_peers
166
+ self.blit_header(:list_peers)
167
+ end
168
+
169
+ #----------------------------------------------------------------------
170
+ # Convenience methods for blit clients
171
+ #----------------------------------------------------------------------
172
+
173
+ BLIT_HANDLERS = {
174
+ :TCP => lambda {|msg|
175
+ s=TCPSocket.new(@blit_addr, @blit_port)
176
+ wl=s.write(msg)
177
+ s.close
178
+ return wl
179
+ },
180
+ :UDP => lambda {|msg|
181
+ s=UDPSocket.new
182
+ wl=s.send( msg, 0, @blit_addr, @blit_port)
183
+ s.close
184
+ return wl
185
+ }
186
+ }
187
+
188
+ def self.blit_init(opts={})
189
+ @blit_addr = (opts[:addr] || DEFAULT_IPADDR)
190
+ @blit_port = (opts[:port] || DEFAULT_PORT)
191
+ proto = (opts[:protocol] || DEFAULT_PROTOCOL)
192
+ @blit_handler = BLIT_HANDLERS[ proto ]
193
+ raise "invalid blit transport protocol" unless @blit_handler
194
+ end
195
+
196
+ def self.initialized?
197
+ @blit_addr and @blit_port and @blit_handler
198
+ end
199
+
200
+ def self.blit_send(data, idx=0)
201
+ msg = make_sendmsg(idx, data)
202
+ blit_raw(msg)
203
+ end
204
+
205
+ def self.blit_raw(buf)
206
+ raise "use blit_init first!" unless self.initialized?
207
+ @blit_handler.call buf
208
+ end
209
+
210
+ end # of module Blit
211
+
212
+
213
+ end # of module Plug
214
+
215
+ class String
216
+ #----------------------------------------------------------------------
217
+ # A Blit sender convenience method for strings
218
+ def blit(idx=0)
219
+ raise "blit must be initialized with blit_init" unless Plug::Blit.initialized?
220
+ Plug::Blit.blit_send(self, idx)
221
+ end
222
+ end
@@ -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
+