class Rex::Proto::Http::Server

Acts as an HTTP server, processing requests and dispatching them to registered procs. Some of this server was modeled after webrick.

Constants

DefaultServer

The default server name that will be returned in the Server attribute of a response.

ExtensionMimeTypes

A hash that associated a file extension with a mime type for use as the content type of responses.

Attributes

comm[RW]
context[RW]
listen_host[RW]
listen_port[RW]
listener[RW]
resources[RW]
server_name[RW]
ssl[RW]
ssl_cert[RW]
ssl_compression[RW]

Public Class Methods

hardcore_alias(*args) click to toggle source

Returns the hardcore alias for the HTTP service

# File lib/rex/proto/http/server.rb, line 125
def self.hardcore_alias(*args)
  "#{(args[0] || '')}#{(args[1] || '')}"
end
new(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil, ssl_cert = nil, ssl_compression = false) click to toggle source

Initializes an HTTP server as listening on the provided port and hostname.

# File lib/rex/proto/http/server.rb, line 101
def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil, ssl_cert = nil, ssl_compression = false)
  self.listen_host     = listen_host
  self.listen_port     = port
  self.ssl             = ssl
  self.context         = context
  self.comm            = comm
  self.ssl_cert        = ssl_cert
  self.ssl_compression = ssl_compression
  self.listener        = nil
  self.resources       = {}
  self.server_name     = DefaultServer
end

Public Instance Methods

add_resource(name, opts) click to toggle source

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 following:

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.
# File lib/rex/proto/http/server.rb, line 208
def add_resource(name, opts)
  if (resources[name])
    raise RuntimeError,
      "The supplied resource '#{name}' is already added.", caller
  end

  # If a procedure was passed, mount the resource with it.
  if (opts['Proc'])
    mount(name, Handler::Proc, false, opts['Proc'], opts['VirtualDirectory'])
  else
    raise ArgumentError, "You must specify a procedure."
  end
end
add_response_headers(resp) click to toggle source

Adds Server headers and stuff.

# File lib/rex/proto/http/server.rb, line 232
def add_response_headers(resp)
  resp['Server'] = self.server_name if not resp['Server']
end
alias() click to toggle source

HTTP server.

Calls superclass method
# File lib/rex/proto/http/server.rb, line 132
def alias
  super || "HTTP Server"
end
close_client(cli) click to toggle source

Closes the supplied client, if valid.

# File lib/rex/proto/http/server.rb, line 181
def close_client(cli)
  listener.close_client(cli)
end
inspect() click to toggle source

More readable inspect that only shows the url and resources @return [String]

# File lib/rex/proto/http/server.rb, line 116
def inspect
  resources_str = resources.keys.map{|r| r.inspect }.join ", "

  "#<#{self.class} http#{ssl ? "s" : ""}://#{listen_host}:#{listen_port} [ #{resources_str} ]>"
end
mime_type(file) click to toggle source

Returns the mime type associated with the supplied file. Right now the set of mime types is fairly limited.

# File lib/rex/proto/http/server.rb, line 240
def mime_type(file)
  type = nil

  if (file =~ /\.(.+?)$/)
    type = ExtensionMimeTypes[$1.downcase]
  end

  type || "text/plain"
end
mount(root, handler, long_call = false, *args) click to toggle source

Mounts a directory or resource as being serviced by the supplied handler.

# File lib/rex/proto/http/server.rb, line 188
def mount(root, handler, long_call = false, *args)
  resources[root] = [ handler, long_call, args ]
end
remove_resource(name) click to toggle source

Removes the supplied resource handler.

# File lib/rex/proto/http/server.rb, line 225
def remove_resource(name)
  self.resources.delete(name)
end
send_e404(cli, request) click to toggle source

Sends a 404 error to the client for a given request.

# File lib/rex/proto/http/server.rb, line 253
def send_e404(cli, request)
  resp = Response::E404.new

  resp['Content-Type'] = 'text/html'

  resp.body =
    "<html><head>" +
    "<title>404 Not Found</title>" +
    "</head><body>" +
    "<h1>Not found</h1>" +
    "The requested URL #{html_escape(request.resource)} was not found on this server.<p><hr>" +
    "</body></html>"

  # Send the response to the client like what
  cli.send_response(resp)
end
start() click to toggle source

Listens on the defined port and host and starts monitoring for clients.

# File lib/rex/proto/http/server.rb, line 139
def start

  self.listener = Rex::Socket::TcpServer.create(
    'LocalHost' => self.listen_host,
    'LocalPort' => self.listen_port,
    'Context'   => self.context,
    'SSL'             => self.ssl,
    'SSLCert' => self.ssl_cert,
    'SSLCompression' => self.ssl_compression,
    'Comm'      => self.comm
  )

  # Register callbacks
  self.listener.on_client_connect_proc = Proc.new { |cli|
    on_client_connect(cli)
  }
  self.listener.on_client_data_proc = Proc.new { |cli|
    on_client_data(cli)
  }

  self.listener.start
end
stop() click to toggle source

Terminates the monitor thread and turns off the listener.

# File lib/rex/proto/http/server.rb, line 165
def stop
  self.listener.stop
  self.listener.close
end
unmount(root) click to toggle source

Remove the mount point.

# File lib/rex/proto/http/server.rb, line 195
def unmount(root)
  resources.delete(root)
end
wait() click to toggle source

Waits for the HTTP service to terminate

# File lib/rex/proto/http/server.rb, line 174
def wait
  self.listener.wait if self.listener
end

Protected Instance Methods

dispatch_request(cli, request) click to toggle source

Dispatches the supplied request for a given connection.

# File lib/rex/proto/http/server.rb, line 322
def dispatch_request(cli, request)
  # Is the client requesting keep-alive?
  if ((request['Connection']) and
     (request['Connection'].downcase == 'Keep-Alive'.downcase))
    cli.keepalive = true
  end

  # 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
  }

  if (p)
    # 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 (handler.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)
      Rex::ThreadFactory.spawn("HTTPServerRequestHandler", false) {
        handler.on_request(cli, request)
      }
    else
      handler.on_request(cli, request)
    end
  else
    elog("Failed to find handler for resource: #{request.resource}",
      LogSource)

    send_e404(cli, request)
  end

  # If keep-alive isn't enabled for this client, close the connection
  if (cli.keepalive == false)
    close_client(cli)
  end
end
on_client_connect(cli) click to toggle source

Extends new clients with the ServerClient module and initializes them.

# File lib/rex/proto/http/server.rb, line 279
def on_client_connect(cli)
  cli.extend(ServerClient)

  cli.init_cli(self)
end
on_client_data(cli) click to toggle source

Processes data coming in from a client.

# File lib/rex/proto/http/server.rb, line 288
def on_client_data(cli)
  begin
    data = cli.read(65535)

    raise ::EOFError if not data
    raise ::EOFError if data.empty?

    case cli.request.parse(data)
      when Packet::ParseCode::Completed
        dispatch_request(cli, cli.request)
        cli.reset_cli

      when Packet::ParseCode::Partial
        # Return and wait for the on_client_data handler to be called again
        # The Request object tracks the state of the request for us
        return

      when Packet::ParseCode::Error
        close_client(cli)
    end
  rescue EOFError
    if (cli.request.completed?)
      dispatch_request(cli, cli.request)

      cli.reset_cli
    end

    close_client(cli)
  end
end