enhanced http server crap
git-svn-id: file:///home/svn/incoming/trunk@3063 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
2f0b44adf6
commit
fc9376d385
|
@ -68,7 +68,8 @@ class Driver < Msf::Ui::Driver
|
|||
ilog("Web server started on #{host}:#{port}", LogSource)
|
||||
|
||||
service.add_resource(
|
||||
opts['ServerRoot'] || DefaultRoot,
|
||||
server_root,
|
||||
'Directory' => true,
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request(cli, req)
|
||||
})
|
||||
|
@ -91,6 +92,13 @@ class Driver < Msf::Ui::Driver
|
|||
term_event.set
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the root resource name, such as '/msfweb'
|
||||
#
|
||||
def server_root
|
||||
opts['ServerRoot'] || DefaultRoot
|
||||
end
|
||||
|
||||
#
|
||||
# The framework instance associated with this driver.
|
||||
#
|
||||
|
@ -125,7 +133,9 @@ protected
|
|||
# dispatched.
|
||||
#
|
||||
def on_request(cli, req)
|
||||
dispatch_request(cli, req)
|
||||
parts = req.resource.gsub(server_root, '').split(/\//)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -27,6 +27,8 @@ require 'rex/socket/comm/local.rb.ut'
|
|||
require 'rex/socket/switch_board.rb.ut'
|
||||
require 'rex/socket/subnet_walker.rb.ut'
|
||||
|
||||
require 'rex/proto/http'
|
||||
|
||||
require 'rex/parser/arguments.rb.ut'
|
||||
|
||||
require 'rex/ui/text/color.rb.ut'
|
||||
|
@ -67,6 +69,15 @@ class Rex::TestSuite
|
|||
suite << Rex::Socket::SwitchBoard::UnitTest.suite
|
||||
suite << Rex::Socket::SubnetWalker::UnitTest.suite
|
||||
|
||||
# Protocols
|
||||
suite << Rex::Proto::Http::Client::UnitTest.suite
|
||||
suite << Rex::Proto::Http::Server::UnitTest.suite
|
||||
suite << Rex::Proto::Http::Packet::UnitTest.suite
|
||||
suite << Rex::Proto::Http::Request::UnitTest.suite
|
||||
suite << Rex::Proto::Http::Response::UnitTest.suite
|
||||
suite << Rex::Proto::Http::Handler::Erb::UnitTest.suite
|
||||
suite << Rex::Proto::Http::Handler::Proc::UnitTest.suite
|
||||
|
||||
# Parsers
|
||||
suite << Rex::Parser::Arguments::UnitTest.suite
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
module Rex
|
||||
module Proto
|
||||
module Http
|
||||
|
||||
###
|
||||
#
|
||||
# This class acts as the base class for all handlers.
|
||||
#
|
||||
###
|
||||
class Handler
|
||||
|
||||
require 'rex/proto/http/handler/erb'
|
||||
require 'rex/proto/http/handler/proc'
|
||||
|
||||
#
|
||||
# Initializes the handler instance as being associated with the supplied
|
||||
# server.
|
||||
#
|
||||
def initialize(server)
|
||||
self.server = server
|
||||
end
|
||||
|
||||
#
|
||||
# By default, handlers do not require a relative resource.
|
||||
#
|
||||
def self.relative_resource_required?
|
||||
false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :server # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,105 @@
|
|||
require 'erb'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Http
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a handler for ERB (.rhtml) template files. This is
|
||||
# based off the webrick handler.
|
||||
#
|
||||
###
|
||||
class Handler::Erb < Handler
|
||||
|
||||
#
|
||||
# ERB handlers required a relative resource so that the full path name can
|
||||
# be computed.
|
||||
#
|
||||
def self.relative_resource_required?
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the ERB handler
|
||||
#
|
||||
def initialize(server, root_path, opts = {})
|
||||
super(server)
|
||||
|
||||
self.root_path = root_path
|
||||
self.opts = opts
|
||||
|
||||
self.opts['MimeType'] = "text/html" unless self.opts['MimeType']
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a request arrives.
|
||||
#
|
||||
def on_request(cli, req)
|
||||
resource = req.relative_resource
|
||||
|
||||
# Make sure directory traversals aren't happening
|
||||
if (resource =~ /\.\./)
|
||||
wlog("Erb::on_request: Dangerous request performed: #{resource}",
|
||||
LogSource)
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
resp = Response.new
|
||||
|
||||
# Calculate the actual file path on disk.
|
||||
file_path = root_path + resource
|
||||
|
||||
puts "file path is #{file_path}"
|
||||
|
||||
# Serialize the contents of the file
|
||||
data = ::IO.readlines(file_path).join
|
||||
|
||||
# Evaluate the data and set the output as the response body.
|
||||
resp.body = evaluate(ERB.new(data), cli, req, resp)
|
||||
|
||||
# Set the content-type to text/html by default.
|
||||
resp['Content-Type'] = opts['MimeType']
|
||||
rescue
|
||||
elog("Erb::on_request: #{$!}\n\n#{$@.join("\n")}", LogSource)
|
||||
|
||||
puts "exception: #{$!} #{$@.join("\n")}"
|
||||
|
||||
resp = Response::E404.new
|
||||
end
|
||||
|
||||
# Send the response to the
|
||||
if (cli and resp)
|
||||
cli.send_response(resp)
|
||||
end
|
||||
|
||||
resp
|
||||
end
|
||||
|
||||
#
|
||||
# Evaulates the ERB context in a specific binding context.
|
||||
#
|
||||
def evaluate(erb, cli, request, response)
|
||||
# If the thing that created this handler wanted us to use a callback
|
||||
# instead of the default behavior, then let's do that.
|
||||
if (opts['Callback'])
|
||||
opts['Callback'].call(erb, cli, request, response)
|
||||
else
|
||||
Module.new.module_eval {
|
||||
query_string = request.qstring
|
||||
meta_vars = request.meta_vars
|
||||
erb.result(binding)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :root_path, :opts # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', '..'))
|
||||
|
||||
require 'test/unit'
|
||||
require 'rex/proto/http'
|
||||
|
||||
class Rex::Proto::Http::Handler::Erb::UnitTest < Test::Unit::TestCase
|
||||
|
||||
Klass = Rex::Proto::Http::Handler::Erb
|
||||
Request = Rex::Proto::Http::Request
|
||||
|
||||
def test_erb
|
||||
k = Klass.new(nil, File.dirname(__FILE__))
|
||||
r = k.on_request(nil, Request::Get.new("/erb.rb.ut.rb.rhtml"))
|
||||
|
||||
assert_not_nil(r)
|
||||
assert_equal("foo 4\n", r.body)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
foo <%= 2 + 2 %>
|
|
@ -0,0 +1,43 @@
|
|||
require 'erb'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Http
|
||||
|
||||
###
|
||||
#
|
||||
# This class is used to wrapper the calling of a procedure when a request
|
||||
# arrives.
|
||||
#
|
||||
###
|
||||
class Handler::Proc < Handler
|
||||
|
||||
#
|
||||
# Initializes the proc handler with the supplied procedure
|
||||
#
|
||||
def initialize(server, procedure)
|
||||
super(server)
|
||||
|
||||
self.procedure = procedure
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a request arrives.
|
||||
#
|
||||
def on_request(cli, req)
|
||||
begin
|
||||
procedure.call(cli, req)
|
||||
rescue
|
||||
elog("Proc::on_request: #{$!}\n\n#{$@.join("\n")}", LogSource)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :procedure # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..', '..'))
|
||||
|
||||
require 'test/unit'
|
||||
require 'rex/proto/http'
|
||||
|
||||
class Rex::Proto::Http::Handler::Proc::UnitTest < Test::Unit::TestCase
|
||||
|
||||
Klass = Rex::Proto::Http::Handler::Proc
|
||||
Request = Rex::Proto::Http::Request
|
||||
|
||||
def test_proc
|
||||
cool = 0
|
||||
k = Klass.new(nil, Proc.new { |cli, req|
|
||||
cool = 1
|
||||
})
|
||||
|
||||
r = k.on_request(nil, Request::Get.new("/erb.rb.ut.rb.rhtml"))
|
||||
|
||||
assert_equal(1, cool)
|
||||
end
|
||||
|
||||
end
|
|
@ -57,6 +57,7 @@ class Request < Packet
|
|||
self.uri_parts = {}
|
||||
self.proto = proto || DefaultProtocol
|
||||
|
||||
update_uri_parts
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -68,21 +69,31 @@ class Request < Packet
|
|||
self.uri = URI.decode(md[2])
|
||||
self.proto = md[3]
|
||||
|
||||
# If it has a query string, get the parts.
|
||||
if ((self.uri) and (md = self.uri.match(/(.+?)\?(.*)$/)))
|
||||
self.uri_parts['QueryString'] = parse_cgi_qstring(md[2])
|
||||
self.uri_parts['Resource'] = md[1]
|
||||
# Otherwise, just assume that the URI is equal to the resource being
|
||||
# requested.
|
||||
else
|
||||
self.uri_parts['QueryString'] = nil
|
||||
self.uri_parts['Resource'] = self.uri
|
||||
end
|
||||
update_uri_parts
|
||||
else
|
||||
raise RuntimeError, "Invalid request command string", caller
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Split the URI into the resource being requested and its query string.
|
||||
#
|
||||
def update_uri_parts
|
||||
# If it has a query string, get the parts.
|
||||
if ((self.uri) and (md = self.uri.match(/(.+?)\?(.*)$/)))
|
||||
self.uri_parts['QueryString'] = parse_cgi_qstring(md[2])
|
||||
self.uri_parts['Resource'] = md[1]
|
||||
# Otherwise, just assume that the URI is equal to the resource being
|
||||
# requested.
|
||||
else
|
||||
self.uri_parts['QueryString'] = nil
|
||||
self.uri_parts['Resource'] = self.uri
|
||||
end
|
||||
|
||||
# Set the relative resource to the actual resource.
|
||||
self.relative_resource = resource
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the command string derived from the three values.
|
||||
#
|
||||
|
@ -97,6 +108,14 @@ class Request < Packet
|
|||
self.uri_parts['Resource']
|
||||
end
|
||||
|
||||
#
|
||||
# Changes the resource URI. This is used when making a request relative to
|
||||
# a given mount point.
|
||||
#
|
||||
def resource=(rsrc)
|
||||
self.uri_parts['Resource'] = rsrc
|
||||
end
|
||||
|
||||
#
|
||||
# If there were CGI parameters in the URI, this will hold a hash of each
|
||||
# variable to value. If there is more than one value for a given variable,
|
||||
|
@ -106,6 +125,15 @@ class Request < Packet
|
|||
self.uri_parts['QueryString']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a hash of variables that contain information about the request,
|
||||
# such as the remote host information.
|
||||
#
|
||||
# TODO
|
||||
#
|
||||
def meta_vars
|
||||
end
|
||||
|
||||
#
|
||||
# The method being used for the request (e.g. GET).
|
||||
#
|
||||
|
@ -122,6 +150,10 @@ class Request < Packet
|
|||
# The protocol to be sent with the request.
|
||||
#
|
||||
attr_accessor :proto
|
||||
#
|
||||
# The resource path relative to the root of a server mount point.
|
||||
#
|
||||
attr_accessor :relative_resource
|
||||
|
||||
protected
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'rex/socket'
|
||||
require 'rex/proto/http'
|
||||
require 'rex/proto/http/handler'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
|
@ -61,7 +62,7 @@ end
|
|||
###
|
||||
#
|
||||
# Acts as an HTTP server, processing requests and dispatching them to
|
||||
# registered procs.
|
||||
# registered procs. Some of this server was modeled after webrick.
|
||||
#
|
||||
###
|
||||
class Server
|
||||
|
@ -118,6 +119,20 @@ class Server
|
|||
listener.close_client(cli)
|
||||
end
|
||||
|
||||
#
|
||||
# Mounts a directory or resource as being serviced by the supplied handler.
|
||||
#
|
||||
def mount(root, handler, long_call = false, *args)
|
||||
resources[root] = [ handler, long_call, args ]
|
||||
end
|
||||
|
||||
#
|
||||
# Remove the mount point.
|
||||
#
|
||||
def unmount(root)
|
||||
resources.delete(root)
|
||||
end
|
||||
|
||||
#
|
||||
# Adds a resource handler, such as one for /, which will be called whenever
|
||||
# the resource is requested. The ``opts'' parameter can have any of the
|
||||
|
@ -126,15 +141,19 @@ class Server
|
|||
# Proc (proc) - The procedure to call when a request comes in for this resource.
|
||||
# LongCall (bool) - Hints to the server that this resource may have long
|
||||
# request processing times.
|
||||
# Directory (bool) - Whether or not this resource symbolizes a directory.
|
||||
#
|
||||
def add_resource(name, opts)
|
||||
if (self.resources[name])
|
||||
if (resources[name])
|
||||
raise RuntimeError,
|
||||
"The supplied resource '#{name}' is already added.", caller
|
||||
end
|
||||
|
||||
self.resources[name] = opts
|
||||
# If a procedure was passed, mount the resource with it.
|
||||
if (opts['Proc'])
|
||||
mount(name, Handler::Proc, false, opts['Proc'])
|
||||
else
|
||||
raise ArgumentError, "You must specify a procedure."
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -199,29 +218,47 @@ protected
|
|||
(request['Connection'].downcase == 'Keep-Alive'.downcase))
|
||||
cli.keepalive = true
|
||||
end
|
||||
|
||||
# If we fail to find the resource by full name, check if
|
||||
# it's a directory resource
|
||||
if ((p = self.resources[request.resource]) == nil)
|
||||
resources.each_pair { |k, val|
|
||||
if (val['Directory'] and request.resource =~ /^#{k}/)
|
||||
p = val
|
||||
break
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# If we found the resource handler for this resource, call its
|
||||
# procedure.
|
||||
# Search for the resource handler for the requested URL. This is pretty
|
||||
# inefficient right now, but we can spruce it up later.
|
||||
p = nil
|
||||
len = 0
|
||||
root = nil
|
||||
|
||||
resources.each_pair { |k, val|
|
||||
if (request.resource =~ /^#{k}/ and k.length > len)
|
||||
p = val
|
||||
len = k.length
|
||||
root = k
|
||||
end
|
||||
}
|
||||
|
||||
begin
|
||||
if (p)
|
||||
if (p['LongCall'] == true)
|
||||
# Create an instance of the handler for this resource
|
||||
handler = p[0].new(self, *p[2])
|
||||
|
||||
# If the handler class requires a relative resource...
|
||||
if (p[0].relative_resource_required?)
|
||||
# Substituted the mount point root in the request to make things
|
||||
# relative to the mount point.
|
||||
request.relative_resource = request.resource.gsub(root, '')
|
||||
request.relative_resource = '/' + request.relative_resource if (request.relative_resource !~ /^\//)
|
||||
end
|
||||
|
||||
# If we found the resource handler for this resource, call its
|
||||
# procedure.
|
||||
if (p[1] == true)
|
||||
Thread.new {
|
||||
p['Proc'].call(cli, request)
|
||||
handler.on_request(cli, request)
|
||||
}
|
||||
else
|
||||
p['Proc'].call(cli, request)
|
||||
handler.on_request(cli, request)
|
||||
end
|
||||
else
|
||||
elog("Failed to find handler for resource: #{request.resource}",
|
||||
LogSource)
|
||||
|
||||
send_e404(cli, request)
|
||||
end
|
||||
|
||||
|
@ -229,6 +266,9 @@ protected
|
|||
if (cli.keepalive == false)
|
||||
close_client(cli)
|
||||
end
|
||||
rescue
|
||||
puts "bleh #{$!} #{$@.join("\n")}"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -52,7 +52,7 @@ class Rex::Proto::Http::Server::UnitTest < Test::Unit::TestCase
|
|||
}
|
||||
|
||||
s.remove_resource('/foo')
|
||||
|
||||
|
||||
req = Rex::Proto::Http::Request::Get.new('/foo')
|
||||
res = c.send_request(req)
|
||||
assert_not_nil(res)
|
||||
|
|
|
@ -21,7 +21,7 @@ class Rex::Socket::SslTcp::UnitTest < Test::Unit::TestCase
|
|||
# Send a HEAD request and make sure we get some kind of response
|
||||
head_request = "HEAD / HTTP/1.0\r\n\r\n"
|
||||
|
||||
assert_equal(true, t.put(head_request), "sending head request")
|
||||
assert_equal(19, t.put(head_request), "sending head request")
|
||||
|
||||
head_response = ""
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class Rex::Socket::TcpServer::UnitTest < Test::Unit::TestCase
|
|||
scli = serv.accept
|
||||
assert_kind_of(Rex::Socket::Tcp, scli, "valid server client Tcp")
|
||||
|
||||
assert_equal(true, scli.put("Yo"), "scli: put Yo")
|
||||
assert_equal(2, scli.put("Yo"), "scli: put Yo")
|
||||
assert_equal("Yo", ccli.get(), "ccli: get Yo")
|
||||
ensure
|
||||
ccli.close if (ccli)
|
||||
|
|
Loading…
Reference in New Issue