Class: Rex::Proto::Http::Server

Inherits:
Object
  • Object
show all
Includes:
Rex::Proto
Defined in:
lib/rex/proto/http/server.rb

Overview

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

Constant Summary collapse

ExtensionMimeTypes =

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

{
  "rhtml" => "text/html",
  "html"  => "text/html",
  "htm"   => "text/htm",
  "jpg"   => "image/jpeg",
  "jpeg"  => "image/jpeg",
  "gif"   => "image/gif",
  "png"   => "image/png",
  "bmp"   => "image/bmp",
  "txt"   => "text/plain",
  "css"   => "text/css",
  "ico"   => "image/x-icon",
}
DefaultServer =

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

"Rex"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil, ssl_cert = nil, ssl_compression = false, ssl_cipher = nil, ssl_version = nil) ⇒ Server

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



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/rex/proto/http/server.rb', line 48

def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {},
               comm = nil, ssl_cert = nil, ssl_compression = false,
               ssl_cipher = nil, ssl_version = nil)
  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.ssl_cipher      = ssl_cipher
  self.ssl_version     = ssl_version
  self.listener        = nil
  self.resources       = {}
  self.server_name     = DefaultServer
end

Instance Attribute Details

#commObject

Returns the value of attribute comm.



223
224
225
# File 'lib/rex/proto/http/server.rb', line 223

def comm
  @comm
end

#contextObject

Returns the value of attribute context.



223
224
225
# File 'lib/rex/proto/http/server.rb', line 223

def context
  @context
end

#listen_hostObject

Returns the value of attribute listen_host.



223
224
225
# File 'lib/rex/proto/http/server.rb', line 223

def listen_host
  @listen_host
end

#listen_portObject

Returns the value of attribute listen_port.



223
224
225
# File 'lib/rex/proto/http/server.rb', line 223

def listen_port
  @listen_port
end

#listenerObject

Returns the value of attribute listener.



225
226
227
# File 'lib/rex/proto/http/server.rb', line 225

def listener
  @listener
end

#resourcesObject

Returns the value of attribute resources.



225
226
227
# File 'lib/rex/proto/http/server.rb', line 225

def resources
  @resources
end

#server_nameObject

Returns the value of attribute server_name.



223
224
225
# File 'lib/rex/proto/http/server.rb', line 223

def server_name
  @server_name
end

#sslObject

Returns the value of attribute ssl.



224
225
226
# File 'lib/rex/proto/http/server.rb', line 224

def ssl
  @ssl
end

#ssl_certObject

Returns the value of attribute ssl_cert.



224
225
226
# File 'lib/rex/proto/http/server.rb', line 224

def ssl_cert
  @ssl_cert
end

#ssl_cipherObject

Returns the value of attribute ssl_cipher.



224
225
226
# File 'lib/rex/proto/http/server.rb', line 224

def ssl_cipher
  @ssl_cipher
end

#ssl_compressionObject

Returns the value of attribute ssl_compression.



224
225
226
# File 'lib/rex/proto/http/server.rb', line 224

def ssl_compression
  @ssl_compression
end

#ssl_versionObject

Returns the value of attribute ssl_version.



224
225
226
# File 'lib/rex/proto/http/server.rb', line 224

def ssl_version
  @ssl_version
end

Class Method Details

.hardcore_alias(*args) ⇒ Object

Returns the hardcore alias for the HTTP service



76
77
78
# File 'lib/rex/proto/http/server.rb', line 76

def self.hardcore_alias(*args)
  "#{(args[0] || '')}-#{(args[1] || '')}-#{args[4] || ''}"
end

Instance Method Details

#add_resource(name, opts) ⇒ Object

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.


161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rex/proto/http/server.rb', line 161

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) ⇒ Object

Adds Server headers and stuff.



185
186
187
# File 'lib/rex/proto/http/server.rb', line 185

def add_response_headers(resp)
  resp['Server'] = self.server_name if not resp['Server']
end

#aliasObject

HTTP server.



83
84
85
# File 'lib/rex/proto/http/server.rb', line 83

def alias
  super || "HTTP Server"
end

#close_client(cli) ⇒ Object

Closes the supplied client, if valid.



134
135
136
# File 'lib/rex/proto/http/server.rb', line 134

def close_client(cli)
  listener.close_client(cli)
end

#dispatch_request(cli, request) ⇒ Object (protected)

Dispatches the supplied request for a given connection.



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/rex/proto/http/server.rb', line 275

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

#inspectString

More readable inspect that only shows the url and resources

Returns:

  • (String)


67
68
69
70
71
# File 'lib/rex/proto/http/server.rb', line 67

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) ⇒ Object

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



193
194
195
196
197
198
199
200
201
# File 'lib/rex/proto/http/server.rb', line 193

def mime_type(file)
  type = nil

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

  type || "text/plain"
end

#mount(root, handler, long_call = false, *args) ⇒ Object

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



141
142
143
# File 'lib/rex/proto/http/server.rb', line 141

def mount(root, handler, long_call = false, *args)
  resources[root] = [ handler, long_call, args ]
end

#on_client_connect(cli) ⇒ Object (protected)

Extends new clients with the ServerClient module and initializes them.



232
233
234
235
236
# File 'lib/rex/proto/http/server.rb', line 232

def on_client_connect(cli)
  cli.extend(ServerClient)

  cli.init_cli(self)
end

#on_client_data(cli) ⇒ Object (protected)

Processes data coming in from a client.



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/rex/proto/http/server.rb', line 241

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

#remove_resource(name) ⇒ Object

Removes the supplied resource handler.



178
179
180
# File 'lib/rex/proto/http/server.rb', line 178

def remove_resource(name)
  self.resources.delete(name)
end

#send_e404(cli, request) ⇒ Object

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



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/rex/proto/http/server.rb', line 206

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

#startObject

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



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/rex/proto/http/server.rb', line 90

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,
    'SSLCipher'      => self.ssl_cipher,
    'SSLVersion'     => self.ssl_version,
    '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

#stopObject

Terminates the monitor thread and turns off the listener.



118
119
120
121
# File 'lib/rex/proto/http/server.rb', line 118

def stop
  self.listener.stop
  self.listener.close
end

#unmount(root) ⇒ Object

Remove the mount point.



148
149
150
# File 'lib/rex/proto/http/server.rb', line 148

def unmount(root)
  resources.delete(root)
end

#waitObject

Waits for the HTTP service to terminate



127
128
129
# File 'lib/rex/proto/http/server.rb', line 127

def wait
  self.listener.wait if self.listener
end