rbkb 0.6.10 → 0.6.11

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.
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.6.11 / 2009-10-06
2
+ * split off rbkb/http as its own package
3
+ * added some new String crypto utilities in extends
4
+
1
5
  == 0.6.10 / 2009-09-30
2
6
  * added -b/--blitsrv argument to plugsrv
3
7
  * added --target-tls/--server-tls arguments to plugsrv for TLS support
data/README.rdoc CHANGED
@@ -17,9 +17,8 @@ related to pen-testing and reversing.
17
17
 
18
18
  rbkb is inspired by Matasano BlackBag (a set of similar tools written in C).
19
19
 
20
- See:
21
- * http://www.matasano.com/log/1048/blackbag-091-new-link-and-minor-fixes/
22
- * http://www.matasano.com/log/552/code-release-blackbag-09-binary-protocol-reversing-unix-thingies/
20
+ See:
21
+ blackbag - http://github.com/emonti/rbkb/raw/master/reference/blackbag-0.9.1.tgz
23
22
 
24
23
  Things go into the black bag as they are stolen (as a compliment!) or dreamed
25
24
  up, usually for simplifying some repetetive task or a desire for a new tool.
@@ -81,10 +80,9 @@ your irb sessions and own scripts. See 'lib_usage.rdoc' for more info.
81
80
 
82
81
  === Gem Installation
83
82
 
84
- rbkb is available as a gem from github:
83
+ rbkb is available as a gem on gemcutter.org:
85
84
 
86
- gem sources -a http://gems.github.com #(you only have to do this once)
87
- gem install emonti-rbkb
85
+ gem install rbkb --source http://gemcutter.org
88
86
 
89
87
 
90
88
  ==== Gem Install Note
@@ -101,7 +99,7 @@ executable bin/* files somewhere unexpected. To find out where these are and
101
99
  either add them to your PATH or copy/symlink them somewhere else like
102
100
  /usr/local/bin/ do this:
103
101
 
104
- gem contents emonti-rbkb
102
+ gem contents rbkb
105
103
 
106
104
 
107
105
  === Manual installation:
data/lib/rbkb.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module Rbkb
3
3
 
4
4
  # :stopdoc:
5
- VERSION = '0.6.10'
5
+ VERSION = '0.6.11'
6
6
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
7
7
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
8
8
  # :startdoc:
data/lib/rbkb/extends.rb CHANGED
@@ -19,7 +19,7 @@ end
19
19
  # Generates a random string of 'size' bytes (8 by default)
20
20
  def random_string(size = 8)
21
21
  chars = (0..255).map {|c| c.chr }
22
- (1..size).collect {|a| chars[rand(chars.size)]}
22
+ (1..size).collect {|a| chars[rand(chars.size)]}.join
23
23
  end
24
24
 
25
25
  # Simple syntactic sugar to pass any object to a block
@@ -194,6 +194,32 @@ class String
194
194
  e
195
195
  end
196
196
 
197
+
198
+ # Produces a character frequency distribution histogram in descending
199
+ # order. Example:
200
+ #
201
+ # pp some_english_text.char_frequency()
202
+ #
203
+ # [[" ", 690],
204
+ # ["e", 354],
205
+ # ["t", 242],
206
+ # ["o", 233],
207
+ # ["i", 218],
208
+ # ["a", 176],
209
+ # ["s", 172],
210
+ # ["r", 172],
211
+ # ["n", 167],
212
+ # ["d", 106],
213
+ # ...
214
+ # ]
215
+ #
216
+ def char_frequency
217
+ hits = {}
218
+ self.each_byte {|c| hits[c.chr] ||= 0; hits[c.chr] += 1 }
219
+ hits.to_a.sort {|a,b| b[1] <=> a[1] }
220
+ end
221
+
222
+
197
223
  # xor against a key. key will be repeated or truncated to self.size.
198
224
  def xor(k)
199
225
  s=self
@@ -205,12 +231,30 @@ class String
205
231
  out.string
206
232
  end
207
233
 
234
+
235
+ # (en|de)ciphers using a substition cipher en/decoder ring in the form of a
236
+ # hash with orig => substitute mappings
237
+ def substitution(keymap)
238
+ split('').map {|c| (sub=keymap[c]) ? sub : c }.join
239
+ end
240
+
241
+
242
+ # (en|de)crypts using a substition xor en/decoder ring in the form of
243
+ # a hash with orig => substitute mappings. Used in conjunction with
244
+ # char_frequency, this sometimes provides a shorter way to derive a single
245
+ # character xor key used in conjunction with char_frequency.
246
+ def substitution_xor(keymap)
247
+ split('').map {|c| (sub=keymap[c]) ? sub.xor(c) : c }.join
248
+ end
249
+
250
+
208
251
  # convert bytes to number then xor against another byte-string or number
209
252
  def ^(x)
210
253
  x = x.dat_to_num unless x.is_a? Numeric
211
254
  (self.dat_to_num ^ x)#.to_bytes
212
255
  end
213
256
 
257
+
214
258
  # Byte rotation as found in lame ciphers.
215
259
  # This was cribbed from Timur Duehr with only a minor change.
216
260
  def rotate_bytes(k=0)
@@ -223,9 +267,11 @@ class String
223
267
  return r
224
268
  end
225
269
 
270
+
226
271
  # String randomizer
227
272
  def randomize ; self.split('').randomize.to_s ; end
228
273
 
274
+
229
275
  # In-place string randomizer
230
276
  def randomize! ; self.replace(randomize) end
231
277
 
@@ -552,7 +598,6 @@ class String
552
598
  end
553
599
  r
554
600
  end
555
-
556
601
 
557
602
 
558
603
  # Returns a reference to actual constant for a given name in namespace
@@ -579,6 +624,22 @@ class Symbol
579
624
  end
580
625
 
581
626
  class Array
627
+
628
+ # Should be in the std library.
629
+ #
630
+ # keys = [:one, :two, :three]
631
+ # vals = [1, 2, 3]
632
+ #
633
+ # keys.zip(vals).to_hash
634
+ # #=> {:two=>2, :three=>3, :one=>1}})
635
+ #
636
+ # keys.to_hash(vals)
637
+ # #=> {:two=>2, :three=>3, :one=>1}})
638
+ def to_hash(vals=nil)
639
+ a = vals ? self.zip(vals) : self
640
+ a.inject({}) {|hash, i| hash[i[0]] = i[1]; hash}
641
+ end
642
+
582
643
  # randomizes the order of contents in the Array (self)
583
644
  def randomize ; self.sort_by { rand } ; end
584
645
 
data/rbkb.gemspec CHANGED
@@ -2,23 +2,23 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{rbkb}
5
- s.version = "0.6.10"
5
+ s.version = "0.6.11"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Eric Monti"]
9
- s.date = %q{2009-10-01}
9
+ s.date = %q{2009-10-07}
10
10
  s.description = %q{Rbkb is a collection of ruby-based pen-testing and reversing tools. Inspired by Matasano Blackbag.}
11
11
  s.email = %q{emonti@matasano.com}
12
12
  s.executables = ["b64", "bgrep", "blit", "c", "crc32", "d64", "dedump", "feed", "hexify", "len", "plugsrv", "rex", "rstrings", "slice", "telson", "unhexify", "urldec", "urlenc", "xor"]
13
13
  s.extra_rdoc_files = ["History.txt", "README.rdoc", "bin/b64", "bin/bgrep", "bin/blit", "bin/c", "bin/crc32", "bin/d64", "bin/dedump", "bin/feed", "bin/hexify", "bin/len", "bin/plugsrv", "bin/rex", "bin/rstrings", "bin/slice", "bin/telson", "bin/unhexify", "bin/urldec", "bin/urlenc", "bin/xor", "cli_usage.rdoc", "lib_usage.rdoc"]
14
- s.files = ["History.txt", "README.rdoc", "Rakefile", "bin/b64", "bin/bgrep", "bin/blit", "bin/c", "bin/crc32", "bin/d64", "bin/dedump", "bin/feed", "bin/hexify", "bin/len", "bin/plugsrv", "bin/rex", "bin/rstrings", "bin/slice", "bin/telson", "bin/unhexify", "bin/urldec", "bin/urlenc", "bin/xor", "cli_usage.rdoc", "doctor-bag.jpg", "lib/rbkb.rb", "lib/rbkb/cli.rb", "lib/rbkb/cli/b64.rb", "lib/rbkb/cli/bgrep.rb", "lib/rbkb/cli/blit.rb", "lib/rbkb/cli/chars.rb", "lib/rbkb/cli/crc32.rb", "lib/rbkb/cli/d64.rb", "lib/rbkb/cli/dedump.rb", "lib/rbkb/cli/feed.rb", "lib/rbkb/cli/hexify.rb", "lib/rbkb/cli/len.rb", "lib/rbkb/cli/rstrings.rb", "lib/rbkb/cli/slice.rb", "lib/rbkb/cli/telson.rb", "lib/rbkb/cli/unhexify.rb", "lib/rbkb/cli/urldec.rb", "lib/rbkb/cli/urlenc.rb", "lib/rbkb/cli/xor.rb", "lib/rbkb/extends.rb", "lib/rbkb/http.rb", "lib/rbkb/http/base.rb", "lib/rbkb/http/body.rb", "lib/rbkb/http/common.rb", "lib/rbkb/http/headers.rb", "lib/rbkb/http/parameters.rb", "lib/rbkb/http/request.rb", "lib/rbkb/http/response.rb", "lib/rbkb/plug.rb", "lib/rbkb/plug/blit.rb", "lib/rbkb/plug/cli.rb", "lib/rbkb/plug/feed_import.rb", "lib/rbkb/plug/peer.rb", "lib/rbkb/plug/plug.rb", "lib/rbkb/plug/proxy.rb", "lib/rbkb/plug/unix_domain.rb", "lib_usage.rdoc", "rbkb.gemspec", "spec/rbkb_spec.rb", "spec/spec_helper.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/test_http.rb", "test/test_http_helper.rb", "test/test_http_request.rb", "test/test_http_response.rb", "test/test_rbkb.rb"]
14
+ s.files = ["History.txt", "README.rdoc", "Rakefile", "bin/b64", "bin/bgrep", "bin/blit", "bin/c", "bin/crc32", "bin/d64", "bin/dedump", "bin/feed", "bin/hexify", "bin/len", "bin/plugsrv", "bin/rex", "bin/rstrings", "bin/slice", "bin/telson", "bin/unhexify", "bin/urldec", "bin/urlenc", "bin/xor", "cli_usage.rdoc", "doctor-bag.jpg", "lib/rbkb.rb", "lib/rbkb/cli.rb", "lib/rbkb/cli/b64.rb", "lib/rbkb/cli/bgrep.rb", "lib/rbkb/cli/blit.rb", "lib/rbkb/cli/chars.rb", "lib/rbkb/cli/crc32.rb", "lib/rbkb/cli/d64.rb", "lib/rbkb/cli/dedump.rb", "lib/rbkb/cli/feed.rb", "lib/rbkb/cli/hexify.rb", "lib/rbkb/cli/len.rb", "lib/rbkb/cli/rstrings.rb", "lib/rbkb/cli/slice.rb", "lib/rbkb/cli/telson.rb", "lib/rbkb/cli/unhexify.rb", "lib/rbkb/cli/urldec.rb", "lib/rbkb/cli/urlenc.rb", "lib/rbkb/cli/xor.rb", "lib/rbkb/extends.rb", "lib/rbkb/plug.rb", "lib/rbkb/plug/blit.rb", "lib/rbkb/plug/cli.rb", "lib/rbkb/plug/feed_import.rb", "lib/rbkb/plug/peer.rb", "lib/rbkb/plug/plug.rb", "lib/rbkb/plug/proxy.rb", "lib/rbkb/plug/unix_domain.rb", "lib_usage.rdoc", "rbkb.gemspec", "spec/rbkb_spec.rb", "spec/spec_helper.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/test_rbkb.rb"]
15
15
  s.homepage = %q{http://emonti.github.com/rbkb}
16
16
  s.rdoc_options = ["--line-numbers", "--main", "README.rdoc"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{rbkb}
19
19
  s.rubygems_version = %q{1.3.4}
20
20
  s.summary = %q{Rbkb is a collection of ruby-based pen-testing and reversing tools}
21
- s.test_files = ["test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/test_http.rb", "test/test_http_helper.rb", "test/test_http_request.rb", "test/test_http_response.rb", "test/test_rbkb.rb"]
21
+ s.test_files = ["test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/test_rbkb.rb"]
22
22
 
23
23
  if s.respond_to? :specification_version then
24
24
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbkb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.10
4
+ version: 0.6.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Monti
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-01 00:00:00 -04:00
12
+ date: 2009-10-07 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -125,14 +125,6 @@ files:
125
125
  - lib/rbkb/cli/urlenc.rb
126
126
  - lib/rbkb/cli/xor.rb
127
127
  - lib/rbkb/extends.rb
128
- - lib/rbkb/http.rb
129
- - lib/rbkb/http/base.rb
130
- - lib/rbkb/http/body.rb
131
- - lib/rbkb/http/common.rb
132
- - lib/rbkb/http/headers.rb
133
- - lib/rbkb/http/parameters.rb
134
- - lib/rbkb/http/request.rb
135
- - lib/rbkb/http/response.rb
136
128
  - lib/rbkb/plug.rb
137
129
  - lib/rbkb/plug/blit.rb
138
130
  - lib/rbkb/plug/cli.rb
@@ -176,10 +168,6 @@ files:
176
168
  - test/test_cli_urlenc.rb
177
169
  - test/test_cli_xor.rb
178
170
  - test/test_helper.rb
179
- - test/test_http.rb
180
- - test/test_http_helper.rb
181
- - test/test_http_request.rb
182
- - test/test_http_response.rb
183
171
  - test/test_rbkb.rb
184
172
  has_rdoc: true
185
173
  homepage: http://emonti.github.com/rbkb
@@ -231,8 +219,4 @@ test_files:
231
219
  - test/test_cli_urlenc.rb
232
220
  - test/test_cli_xor.rb
233
221
  - test/test_helper.rb
234
- - test/test_http.rb
235
- - test/test_http_helper.rb
236
- - test/test_http_request.rb
237
- - test/test_http_response.rb
238
222
  - test/test_rbkb.rb
data/lib/rbkb/http.rb DELETED
@@ -1,21 +0,0 @@
1
-
2
- # ???Why???? would anyone create their own HTTP implementation in ruby with
3
- # so many options out there? Short answer: Net:HTTP and others just don't cut
4
- # it in lots of edge cases. I needed something I could control completely.
5
-
6
- module Rbkb
7
- module Http
8
- VERSION = "0.0.3"
9
- end
10
- end
11
-
12
- require 'time' # gives us Time.httpdate parser and output methods
13
-
14
- require "rbkb/http/common.rb"
15
- require "rbkb/http/base.rb"
16
- require "rbkb/http/request.rb"
17
- require "rbkb/http/response.rb"
18
- require "rbkb/http/headers.rb"
19
- require "rbkb/http/body.rb"
20
- require "rbkb/http/parameters.rb"
21
-
@@ -1,172 +0,0 @@
1
- module Rbkb::Http
2
-
3
- # A base class containing some common features for Request and Response
4
- # objects.
5
- #
6
- # Don't use this class directly, it's intended for being overridden
7
- # from its derived classes or mixins.
8
- class Base
9
- include CommonInterface
10
-
11
- def self.parse(*args)
12
- new(*args)
13
- end
14
-
15
- # Initializes a new Base object
16
- def initialize(*args)
17
- _common_init(*args)
18
- end
19
-
20
- # This method parses just HTTP message body. Expects body to be split
21
- # from the headers before-hand.
22
- def capture_body(bstr)
23
- self.body ||= default_body_obj
24
- @body.capture(bstr)
25
- end
26
-
27
- # XXX stub
28
- def first_entity
29
- @first_entity
30
- end
31
-
32
- # XXX stub
33
- def first_entity=(f)
34
- @first_entity=(f)
35
- end
36
-
37
- # This method parses only HTTP response headers. Expects headers to be
38
- # split from the body before-hand.
39
- def capture_headers(hstr)
40
- self.headers ||= default_headers_obj
41
-
42
- if @body and not @body.capture_complete?
43
- return
44
- elsif @headers.capture_complete?
45
- self.first_entity, @headers = default_headers_obj.capture_full_headers(hstr)
46
- else
47
- @headers.capture(hstr)
48
- end
49
- end
50
-
51
- # This method returns the content length from Headers. This is
52
- # mostly useful if you are using a BoundBody object for the body.
53
- #
54
- # Returns nil if no "Content-Length" is not found.
55
- #
56
- # The opts parameter :ignore_content_length affects this method and
57
- # will cause it always to return nil. This is useful, for example,
58
- # for the responses to the HTTP HEAD request method, which return
59
- # a Content-Length without actual content.
60
- #
61
- def content_length(hdrs=@headers)
62
- raise "headers is nil?" if not hdrs
63
- if( (not @opts[:ignore_content_length]) and
64
- hdrs.get_header_value("Content-Length").to_s =~ /^(\d+)$/ )
65
-
66
- $1.to_i
67
- end
68
- end
69
-
70
- def attach_new_header(hdr_obj=nil)
71
- self.headers = hdr_obj
72
- return hdr_obj
73
- end
74
-
75
- def attach_new_body(body_obj=nil)
76
- self.body = body_obj
77
- return body_obj
78
- end
79
-
80
- # XXX doc override!
81
- def default_headers_obj(*args)
82
- Header.new(*args)
83
- end
84
-
85
- # XXX doc override!
86
- def default_body_obj(*args)
87
- Body.new(*args)
88
- end
89
-
90
- # This method will non-destructively reset the capture state on this
91
- # object and all child entities. Note, however, If child entities are not
92
- # defined, it may instantiate new ones.
93
- # See also: capture_complete?, reset_capture!
94
- def reset_capture
95
- if @headers
96
- @headers.reset_capture if not @headers.capture_complete?
97
- else
98
- attach_new_header()
99
- end
100
-
101
- if @body
102
- @body.reset_capture if not @body.capture_complete?
103
- else
104
- attach_new_body()
105
- end
106
- @capture_state = nil
107
- self
108
- end
109
-
110
- # This method will destructively reset the capture state on this object.
111
- # It does so by initializing fresh child entities and discarding the old
112
- # ones. See also: capture_complete?, reset_capture
113
- def reset_capture!
114
- attach_new_header()
115
- attach_new_body()
116
- @capture_state = nil
117
- self
118
- end
119
-
120
- # Indicates whether this object is ready to capture fresh data, or is
121
- # waiting for additional data or a reset from a previous incomplete or
122
- # otherwise broken capture. See also: reset_capture, reset_capture!
123
- def capture_complete?
124
- if( (@headers and not @headers.capture_complete?) or
125
- (@body and not @body.capture_complete?) )
126
- return false
127
- else
128
- true
129
- end
130
- end
131
-
132
- attr_reader :body, :headers
133
-
134
- # This accessor will attempt to always do the "right thing" while
135
- # setting this object's body entity.
136
- #
137
- # See also: default_body_obj
138
- def body=(b)
139
- if @body
140
- @body.data = b
141
- elsif b.kind_of? Body
142
- @body = b.dup
143
- @body.opts = b.opts
144
- else
145
- @body = default_body_obj(b)
146
- end
147
- @body.base = self
148
- return @body
149
- end
150
-
151
- # This accessor will attempt to always do the "right thing" while
152
- # setting this object's headers entity.
153
- #
154
- # See also: default_headers_obj
155
- def headers=(h)
156
- if @headers
157
- @headers.data = h
158
- elsif h.kind_of? Headers
159
- @headers = h.dup
160
- @headers.opts = h.opts
161
- else
162
- @headers = default_headers_obj(h)
163
- end
164
- @headers.base = self
165
- return @body
166
- end
167
-
168
- end
169
-
170
-
171
- end
172
-
@@ -1,214 +0,0 @@
1
- require 'stringio'
2
-
3
- module Rbkb::Http
4
- class Body < String
5
- include CommonInterface
6
-
7
- def self.parse(str)
8
- new().capture(str)
9
- end
10
-
11
- attr_reader :expect_length
12
-
13
- def initialize(str=nil, opts=nil)
14
- self.opts = opts
15
- if Body === str
16
- self.replace(str)
17
- @opts = str.opts.merge(@opts)
18
- elsif String === str
19
- super(str)
20
- else
21
- super()
22
- end
23
-
24
- yield(self) if block_given?
25
- end
26
-
27
- # The capture method is used when parsing HTTP requests/responses.
28
- # This can and probably should be overridden in derived classes.
29
- def capture(str)
30
- yield(str) if block_given?
31
- self.data=(str)
32
- end
33
-
34
- # The to_raw method is used when writing HTTP requests/responses.
35
- # This can and probably should be overridden in derived classes.
36
- def to_raw
37
- (block_given?) ? yield(self.data) : self.data
38
- end
39
-
40
- attr_reader :base
41
-
42
- def base=(b)
43
- if b.nil? or b.is_a? Base
44
- @base = b
45
- else
46
- raise "base must be a Response or Request object or nil"
47
- end
48
- end
49
-
50
- def data
51
- self
52
- end
53
-
54
- # Sets internal raw string data without any HTTP decoration.
55
- def data=(str)
56
- self.replace(str.to_s)
57
- end
58
-
59
- # Returns the content length from the HTTP base object if
60
- # there is one and content-length is available.
61
- def get_content_length
62
- @base.content_length if @base
63
- end
64
-
65
- # This method will non-destructively reset the capture state on this object.
66
- # It is non-destructive in that it will not affect existing captured data
67
- # if present.
68
- def reset_capture
69
- @expect_length = nil
70
- @base.reset_capture() if @base and @base.capture_complete?
71
- end
72
-
73
- # This method will destructively reset the capture state on this object.
74
- # This method is destructive in that it will clear any previously captured
75
- # data.
76
- def reset_capture!
77
- reset_capture()
78
- self.data=""
79
- end
80
-
81
- def capture_complete?
82
- not @expect_length
83
- end
84
- end
85
-
86
-
87
- # BoundBody is designed for handling an HTTP body when using the usual
88
- # "Content-Length: NNN" HTTP header.
89
- class BoundBody < Body
90
-
91
- # This method may throw :expect_length with one of the following values
92
- # to indicate certain content-length conditions:
93
- #
94
- # > 0 : Got incomplete data in this capture. The object expects
95
- # capture to be called again with more body data.
96
- #
97
- # < 0 : Got more data than expected, the caller should truncate and
98
- # handle the extra data in some way. Note: Calling capture again
99
- # on this instance will start a fresh body capture.
100
- #
101
- # Caller can also detect the above conditions by checking the expect_length
102
- # attribute but should still be prepared handle the throw().
103
- #
104
- # 0/nil: Got exactly what was expected. Caller can proceed with fresh
105
- # captures on this or other Body objects.
106
- #
107
- # See also reset_capture and reset_capture!
108
- def capture(str)
109
- raise "arg 0 must be a string" unless String === str
110
-
111
- # Start fresh unless we're expecting more data
112
- self.data="" unless @expect_length and @expect_length > 0
113
-
114
- if not clen=get_content_length()
115
- raise "content-length is unknown. aborting capture"
116
- else
117
- @expect_length = clen - (self.size + str.size)
118
- self << str[0, clen - self.size]
119
- if @expect_length > 0
120
- throw(:expect_length, @expect_length)
121
- elsif @expect_length < 0
122
- throw(:expect_length, @expect_length)
123
- else
124
- reset_capture()
125
- end
126
- end
127
- return self
128
- end
129
-
130
- def to_raw(*args)
131
- if @base
132
- @base.headers.set_header("Content-Length", self.size)
133
- end
134
- super(*args)
135
- end
136
- end
137
-
138
-
139
- # ChunkedBody is designed for handling an HTTP body when using a
140
- # "Transfer-Encoding: chunked" HTTP header.
141
- class ChunkedBody < Body
142
- DEFAULT_CHUNK_SIZE = 2048
143
-
144
- # Throws :expect_length with 'true' when given incomplete data and expects
145
- # to be called again with more body data to parse.
146
- #
147
- # The caller can also detect this condition by checking the expect_length
148
- # attribute but must still handle the throw().
149
- #
150
- # See also reset_capture and reset_capture!
151
- def capture(str)
152
- # chunked encoding is gross...
153
- if @expect_length
154
- sio = StringIO.new(@last_chunk.to_s + str)
155
- else
156
- sio = StringIO.new(str)
157
- self.data=""
158
- end
159
- @last_chunk = nil
160
-
161
- @expect_length = true
162
- while not sio.eof?
163
- unless m=/^([a-fA-F0-9]+)\s*(;[[:print:]\s]*)?\r?\n$/.match(line=sio.readline)
164
- raise "invalid chunk at #{line.chomp.inspect}"
165
- end
166
- if (chunksz = m[1].hex) == 0
167
- @expect_length = false
168
- # XXX ignore Trailer headers
169
- break
170
- end
171
-
172
- if ( (not sio.eof?) and
173
- (chunk=sio.read(chunksz)) and
174
- chunk.size == chunksz and
175
- (not sio.eof?) and (extra = sio.readline) and
176
- (not sio.eof?) and (extra << sio.readline)
177
- )
178
- if extra =~ /^\r?\n\r?\n$/
179
- yield(chunk) if block_given?
180
- self << chunk
181
- else
182
- raise "expected CRLF"
183
- end
184
- else
185
- @last_chunk = line + chunk.to_s + extra.to_s
186
- break
187
- end
188
- end
189
- throw(:expect_length, @expect_length) if @expect_length
190
- return self
191
- end
192
-
193
-
194
- def to_raw(csz=nil)
195
- csz ||= (@opts[:output_chunk_size] || DEFAULT_CHUNK_SIZE)
196
- unless csz.kind_of? Integer and csz > 0
197
- raise "chunk size must be an integer >= 1"
198
- end
199
-
200
- out=[]
201
- i=0
202
- while i <= self.size
203
- chunk = self[i, csz]
204
- out << "#{chunk.size.to_s(16)}\r\n#{chunk}\r\n\r\n"
205
- yield(self, out.last) if block_given?
206
- i+=csz
207
- end
208
- out << "0\r\n"
209
- yield(self, out.last) if block_given?
210
- return out.join
211
- end
212
- end
213
- end
214
-