Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

server.rb 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. # -*- coding: binary -*-
  2. require 'rex/socket'
  3. require 'rex/proto/http'
  4. require 'rex/proto/http/handler'
  5. module Rex
  6. module Proto
  7. module Http
  8. ###
  9. #
  10. # Runtime extension of the HTTP clients that connect to the server.
  11. #
  12. ###
  13. module ServerClient
  14. #
  15. # Initialize a new request instance.
  16. #
  17. def init_cli(server)
  18. self.request = Request.new
  19. self.server = server
  20. self.keepalive = false
  21. end
  22. #
  23. # Resets the parsing state.
  24. #
  25. def reset_cli
  26. self.request.reset
  27. end
  28. #
  29. # Transmits a response and adds the appropriate headers.
  30. #
  31. def send_response(response)
  32. # Set the connection to close or keep-alive depending on what the client
  33. # can support.
  34. response['Connection'] = (keepalive) ? 'Keep-Alive' : 'close'
  35. # Add any other standard response headers.
  36. server.add_response_headers(response)
  37. # Send it off.
  38. put(response.to_s)
  39. end
  40. #
  41. # The current request context.
  42. #
  43. attr_accessor :request
  44. #
  45. # Boolean that indicates whether or not the connection supports keep-alive.
  46. #
  47. attr_accessor :keepalive
  48. #
  49. # A reference to the server the client is associated with.
  50. #
  51. attr_accessor :server
  52. end
  53. ###
  54. #
  55. # Acts as an HTTP server, processing requests and dispatching them to
  56. # registered procs. Some of this server was modeled after webrick.
  57. #
  58. ###
  59. class Server
  60. include Proto
  61. #
  62. # A hash that associated a file extension with a mime type for use as the
  63. # content type of responses.
  64. #
  65. ExtensionMimeTypes =
  66. {
  67. "rhtml" => "text/html",
  68. "html" => "text/html",
  69. "htm" => "text/htm",
  70. "jpg" => "image/jpeg",
  71. "jpeg" => "image/jpeg",
  72. "gif" => "image/gif",
  73. "png" => "image/png",
  74. "bmp" => "image/bmp",
  75. "txt" => "text/plain",
  76. "css" => "text/css",
  77. "ico" => "image/x-icon",
  78. }
  79. #
  80. # The default server name that will be returned in the Server attribute of
  81. # a response.
  82. #
  83. DefaultServer = "Rex"
  84. #
  85. # Initializes an HTTP server as listening on the provided port and
  86. # hostname.
  87. #
  88. def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {},
  89. comm = nil, ssl_cert = nil, ssl_compression = false,
  90. ssl_cipher = nil)
  91. self.listen_host = listen_host
  92. self.listen_port = port
  93. self.ssl = ssl
  94. self.context = context
  95. self.comm = comm
  96. self.ssl_cert = ssl_cert
  97. self.ssl_compression = ssl_compression
  98. self.ssl_cipher = ssl_cipher
  99. self.listener = nil
  100. self.resources = {}
  101. self.server_name = DefaultServer
  102. end
  103. # More readable inspect that only shows the url and resources
  104. # @return [String]
  105. def inspect
  106. resources_str = resources.keys.map{|r| r.inspect }.join ", "
  107. "#<#{self.class} http#{ssl ? "s" : ""}://#{listen_host}:#{listen_port} [ #{resources_str} ]>"
  108. end
  109. #
  110. # Returns the hardcore alias for the HTTP service
  111. #
  112. def self.hardcore_alias(*args)
  113. "#{(args[0] || '')}#{(args[1] || '')}"
  114. end
  115. #
  116. # HTTP server.
  117. #
  118. def alias
  119. super || "HTTP Server"
  120. end
  121. #
  122. # Listens on the defined port and host and starts monitoring for clients.
  123. #
  124. def start
  125. self.listener = Rex::Socket::TcpServer.create(
  126. 'LocalHost' => self.listen_host,
  127. 'LocalPort' => self.listen_port,
  128. 'Context' => self.context,
  129. 'SSL' => self.ssl,
  130. 'SSLCert' => self.ssl_cert,
  131. 'SSLCompression' => self.ssl_compression,
  132. 'SSLCipher' => self.ssl_cipher,
  133. 'Comm' => self.comm
  134. )
  135. # Register callbacks
  136. self.listener.on_client_connect_proc = Proc.new { |cli|
  137. on_client_connect(cli)
  138. }
  139. self.listener.on_client_data_proc = Proc.new { |cli|
  140. on_client_data(cli)
  141. }
  142. self.listener.start
  143. end
  144. #
  145. # Terminates the monitor thread and turns off the listener.
  146. #
  147. def stop
  148. self.listener.stop
  149. self.listener.close
  150. end
  151. #
  152. # Waits for the HTTP service to terminate
  153. #
  154. def wait
  155. self.listener.wait if self.listener
  156. end
  157. #
  158. # Closes the supplied client, if valid.
  159. #
  160. def close_client(cli)
  161. listener.close_client(cli)
  162. end
  163. #
  164. # Mounts a directory or resource as being serviced by the supplied handler.
  165. #
  166. def mount(root, handler, long_call = false, *args)
  167. resources[root] = [ handler, long_call, args ]
  168. end
  169. #
  170. # Remove the mount point.
  171. #
  172. def unmount(root)
  173. resources.delete(root)
  174. end
  175. #
  176. # Adds a resource handler, such as one for /, which will be called whenever
  177. # the resource is requested. The ``opts'' parameter can have any of the
  178. # following:
  179. #
  180. # Proc (proc) - The procedure to call when a request comes in for this resource.
  181. # LongCall (bool) - Hints to the server that this resource may have long
  182. # request processing times.
  183. #
  184. def add_resource(name, opts)
  185. if (resources[name])
  186. raise RuntimeError,
  187. "The supplied resource '#{name}' is already added.", caller
  188. end
  189. # If a procedure was passed, mount the resource with it.
  190. if (opts['Proc'])
  191. mount(name, Handler::Proc, false, opts['Proc'], opts['VirtualDirectory'])
  192. else
  193. raise ArgumentError, "You must specify a procedure."
  194. end
  195. end
  196. #
  197. # Removes the supplied resource handler.
  198. #
  199. def remove_resource(name)
  200. self.resources.delete(name)
  201. end
  202. #
  203. # Adds Server headers and stuff.
  204. #
  205. def add_response_headers(resp)
  206. resp['Server'] = self.server_name if not resp['Server']
  207. end
  208. #
  209. # Returns the mime type associated with the supplied file. Right now the
  210. # set of mime types is fairly limited.
  211. #
  212. def mime_type(file)
  213. type = nil
  214. if (file =~ /\.(.+?)$/)
  215. type = ExtensionMimeTypes[$1.downcase]
  216. end
  217. type || "text/plain"
  218. end
  219. #
  220. # Sends a 404 error to the client for a given request.
  221. #
  222. def send_e404(cli, request)
  223. resp = Response::E404.new
  224. resp['Content-Type'] = 'text/html'
  225. resp.body =
  226. "<html><head>" +
  227. "<title>404 Not Found</title>" +
  228. "</head><body>" +
  229. "<h1>Not found</h1>" +
  230. "The requested URL #{html_escape(request.resource)} was not found on this server.<p><hr>" +
  231. "</body></html>"
  232. # Send the response to the client like what
  233. cli.send_response(resp)
  234. end
  235. attr_accessor :listen_port, :listen_host, :server_name, :context, :comm
  236. attr_accessor :ssl, :ssl_cert, :ssl_compression, :ssl_cipher
  237. attr_accessor :listener, :resources
  238. protected
  239. #
  240. # Extends new clients with the ServerClient module and initializes them.
  241. #
  242. def on_client_connect(cli)
  243. cli.extend(ServerClient)
  244. cli.init_cli(self)
  245. end
  246. #
  247. # Processes data coming in from a client.
  248. #
  249. def on_client_data(cli)
  250. begin
  251. data = cli.read(65535)
  252. raise ::EOFError if not data
  253. raise ::EOFError if data.empty?
  254. case cli.request.parse(data)
  255. when Packet::ParseCode::Completed
  256. dispatch_request(cli, cli.request)
  257. cli.reset_cli
  258. when Packet::ParseCode::Partial
  259. # Return and wait for the on_client_data handler to be called again
  260. # The Request object tracks the state of the request for us
  261. return
  262. when Packet::ParseCode::Error
  263. close_client(cli)
  264. end
  265. rescue EOFError
  266. if (cli.request.completed?)
  267. dispatch_request(cli, cli.request)
  268. cli.reset_cli
  269. end
  270. close_client(cli)
  271. end
  272. end
  273. #
  274. # Dispatches the supplied request for a given connection.
  275. #
  276. def dispatch_request(cli, request)
  277. # Is the client requesting keep-alive?
  278. if ((request['Connection']) and
  279. (request['Connection'].downcase == 'Keep-Alive'.downcase))
  280. cli.keepalive = true
  281. end
  282. # Search for the resource handler for the requested URL. This is pretty
  283. # inefficient right now, but we can spruce it up later.
  284. p = nil
  285. len = 0
  286. root = nil
  287. resources.each_pair { |k, val|
  288. if (request.resource =~ /^#{k}/ and k.length > len)
  289. p = val
  290. len = k.length
  291. root = k
  292. end
  293. }
  294. if (p)
  295. # Create an instance of the handler for this resource
  296. handler = p[0].new(self, *p[2])
  297. # If the handler class requires a relative resource...
  298. if (handler.relative_resource_required?)
  299. # Substituted the mount point root in the request to make things
  300. # relative to the mount point.
  301. request.relative_resource = request.resource.gsub(/^#{root}/, '')
  302. request.relative_resource = '/' + request.relative_resource if (request.relative_resource !~ /^\//)
  303. end
  304. # If we found the resource handler for this resource, call its
  305. # procedure.
  306. if (p[1] == true)
  307. Rex::ThreadFactory.spawn("HTTPServerRequestHandler", false) {
  308. handler.on_request(cli, request)
  309. }
  310. else
  311. handler.on_request(cli, request)
  312. end
  313. else
  314. elog("Failed to find handler for resource: #{request.resource}",
  315. LogSource)
  316. send_e404(cli, request)
  317. end
  318. # If keep-alive isn't enabled for this client, close the connection
  319. if (cli.keepalive == false)
  320. close_client(cli)
  321. end
  322. end
  323. end
  324. end
  325. end
  326. end