Class: Rex::Proto::Http::Client
- Inherits:
-
Object
- Object
- Rex::Proto::Http::Client
- Defined in:
- lib/rex/proto/http/client.rb
Overview
Acts as a client to an HTTP server, sending requests and receiving responses.
See the RFC: www.w3.org/Protocols/rfc2616/rfc2616.html
Instance Attribute Summary collapse
-
#comm ⇒ Object
An optional comm to use for creating the underlying socket.
-
#config ⇒ Object
The client request configuration.
-
#config_types ⇒ Object
The client request configuration classes.
-
#conn ⇒ Object
The underlying connection.
-
#context ⇒ Object
The calling context to pass to the socket.
-
#hostname ⇒ Object
protected
:nodoc:.
-
#junk_pipeline ⇒ Object
When parsing the request, thunk off the first response from the server, since junk.
-
#kerberos_authenticator ⇒ Object
Auth.
-
#krb_encryptor ⇒ Object
protected
The established kerberos connection info.
-
#local_host ⇒ Object
The local host of the client.
-
#local_port ⇒ Object
The local port of the client.
-
#ntlm_client ⇒ Object
protected
The established NTLM connection info.
-
#password ⇒ Object
Auth.
-
#pipeline ⇒ Object
Whether or not pipelining is in use.
-
#port ⇒ Object
protected
:nodoc:.
-
#proxies ⇒ Object
The proxy list.
-
#ssl ⇒ Object
protected
https.
-
#ssl_version ⇒ Object
protected
https.
-
#subscriber ⇒ Rex::Proto::Http::HttpSubscriber
The HTTP subscriber.
-
#username ⇒ Object
Auth.
Instance Method Summary collapse
-
#_send_recv(req, t = -1,, persist = false) ⇒ Response
Transmit an HTTP request and receive the response.
-
#basic_auth_header(username, password) ⇒ String
Converts username and password into the HTTP Basic authorization string.
- #channel_binding ⇒ Object
-
#close ⇒ Object
Closes the connection to the remote server.
-
#conn? ⇒ Boolean
Returns whether or not the conn is valid.
-
#connect(t = -1)) ⇒ Rex::Socket::Tcp
Connects to the remote server if possible.
-
#digest_auth(opts = {}) ⇒ Response
Send a series of requests to complete Digest Authentication.
-
#initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '', kerberos_authenticator: nil, comm: nil, subscriber: nil) ⇒ Client
constructor
Creates a new client instance.
- #kerberos_auth(opts = {}) ⇒ Object
- #make_cnonce ⇒ Object
-
#negotiate_auth(opts = {}) ⇒ Response
Builds a series of requests to complete Negotiate Auth.
-
#peerinfo ⇒ Object
Target host addr and port for this connection.
-
#pipelining? ⇒ Boolean
Whether or not connections should be pipelined.
-
#read_response(t = -1,, opts = {}) ⇒ Response
Read a response from the server.
-
#request_cgi(opts = {}) ⇒ ClientRequest
Create a CGI compatible request.
-
#request_raw(opts = {}) ⇒ ClientRequest
Create an arbitrary HTTP request.
-
#send_auth(res, opts, t, persist) ⇒ Response
Resends an HTTP Request with the propper authentcation headers set.
-
#send_recv(req, t = -1,, persist = false) ⇒ Response
Sends a request and gets a response back.
-
#send_request(req, t = -1)) ⇒ void
Send an HTTP request to the server.
-
#set_config(opts = {}) ⇒ Object
Set configuration options.
-
#stop ⇒ Object
Cleans up any outstanding connections and other resources.
Constructor Details
#initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '', kerberos_authenticator: nil, comm: nil, subscriber: nil) ⇒ Client
Creates a new client instance
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/rex/proto/http/client.rb', line 26 def initialize(host, port = 80, context = {}, ssl = nil, ssl_version = nil, proxies = nil, username = '', password = '', kerberos_authenticator: nil, comm: nil, subscriber: nil) self.hostname = host self.port = port.to_i self.context = context self.ssl = ssl self.ssl_version = ssl_version self.proxies = proxies self.username = username self.password = password self.kerberos_authenticator = kerberos_authenticator self.comm = comm self.subscriber = subscriber || HttpSubscriber.new # Take ClientRequest's defaults, but override with our own self.config = Http::ClientRequest::DefaultConfig.merge({ 'read_max_data' => (1024*1024*1), 'vhost' => self.hostname, 'ssl_server_name_indication' => self.hostname, }) self.config['agent'] ||= Rex::UserAgent.session_agent # XXX: This info should all be controlled by ClientRequest self.config_types = { 'uri_encode_mode' => ['hex-normal', 'hex-all', 'hex-random', 'hex-noslashes', 'u-normal', 'u-random', 'u-all'], 'uri_encode_count' => 'integer', 'uri_full_url' => 'bool', 'pad_method_uri_count' => 'integer', 'pad_uri_version_count' => 'integer', 'pad_method_uri_type' => ['space', 'tab', 'apache'], 'pad_uri_version_type' => ['space', 'tab', 'apache'], 'method_random_valid' => 'bool', 'method_random_invalid' => 'bool', 'method_random_case' => 'bool', 'version_random_valid' => 'bool', 'version_random_invalid' => 'bool', 'uri_dir_self_reference' => 'bool', 'uri_dir_fake_relative' => 'bool', 'uri_use_backslashes' => 'bool', 'pad_fake_headers' => 'bool', 'pad_fake_headers_count' => 'integer', 'pad_get_params' => 'bool', 'pad_get_params_count' => 'integer', 'pad_post_params' => 'bool', 'pad_post_params_count' => 'integer', 'shuffle_get_params' => 'bool', 'shuffle_post_params' => 'bool', 'uri_fake_end' => 'bool', 'uri_fake_params_start' => 'bool', 'header_folding' => 'bool', 'chunked_size' => 'integer', 'partial' => 'bool' } end |
Instance Attribute Details
#comm ⇒ Object
An optional comm to use for creating the underlying socket.
760 761 762 |
# File 'lib/rex/proto/http/client.rb', line 760 def comm @comm end |
#config ⇒ Object
The client request configuration
764 765 766 |
# File 'lib/rex/proto/http/client.rb', line 764 def config @config end |
#config_types ⇒ Object
The client request configuration classes
768 769 770 |
# File 'lib/rex/proto/http/client.rb', line 768 def config_types @config_types end |
#conn ⇒ Object
The underlying connection.
784 785 786 |
# File 'lib/rex/proto/http/client.rb', line 784 def conn @conn end |
#context ⇒ Object
The calling context to pass to the socket
788 789 790 |
# File 'lib/rex/proto/http/client.rb', line 788 def context @context end |
#hostname ⇒ Object (protected)
:nodoc:
808 809 810 |
# File 'lib/rex/proto/http/client.rb', line 808 def hostname @hostname end |
#junk_pipeline ⇒ Object
When parsing the request, thunk off the first response from the server, since junk
798 799 800 |
# File 'lib/rex/proto/http/client.rb', line 798 def junk_pipeline @junk_pipeline end |
#kerberos_authenticator ⇒ Object
Auth
795 796 797 |
# File 'lib/rex/proto/http/client.rb', line 795 def kerberos_authenticator @kerberos_authenticator end |
#krb_encryptor ⇒ Object (protected)
The established kerberos connection info
818 819 820 |
# File 'lib/rex/proto/http/client.rb', line 818 def krb_encryptor @krb_encryptor end |
#local_host ⇒ Object
The local host of the client.
776 777 778 |
# File 'lib/rex/proto/http/client.rb', line 776 def local_host @local_host end |
#local_port ⇒ Object
The local port of the client.
780 781 782 |
# File 'lib/rex/proto/http/client.rb', line 780 def local_port @local_port end |
#ntlm_client ⇒ Object (protected)
The established NTLM connection info
813 814 815 |
# File 'lib/rex/proto/http/client.rb', line 813 def ntlm_client @ntlm_client end |
#password ⇒ Object
Auth
795 796 797 |
# File 'lib/rex/proto/http/client.rb', line 795 def password @password end |
#pipeline ⇒ Object
Whether or not pipelining is in use.
772 773 774 |
# File 'lib/rex/proto/http/client.rb', line 772 def pipeline @pipeline end |
#port ⇒ Object (protected)
:nodoc:
808 809 810 |
# File 'lib/rex/proto/http/client.rb', line 808 def port @port end |
#proxies ⇒ Object
The proxy list
792 793 794 |
# File 'lib/rex/proto/http/client.rb', line 792 def proxies @proxies end |
#ssl ⇒ Object (protected)
https
806 807 808 |
# File 'lib/rex/proto/http/client.rb', line 806 def ssl @ssl end |
#ssl_version ⇒ Object (protected)
https
806 807 808 |
# File 'lib/rex/proto/http/client.rb', line 806 def ssl_version @ssl_version end |
#subscriber ⇒ Rex::Proto::Http::HttpSubscriber
Returns The HTTP subscriber.
801 802 803 |
# File 'lib/rex/proto/http/client.rb', line 801 def subscriber @subscriber end |
#username ⇒ Object
Auth
795 796 797 |
# File 'lib/rex/proto/http/client.rb', line 795 def username @username end |
Instance Method Details
#_send_recv(req, t = -1,, persist = false) ⇒ Response
Transmit an HTTP request and receive the response
If persist is set, then the request will attempt to reuse an existing connection.
Call this directly instead of #send_recv if you don’t want automatic authentication handling.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/rex/proto/http/client.rb', line 233 def _send_recv(req, t = -1, persist = false) @pipeline = persist subscriber.on_request(req) if req.respond_to?(:opts) && req.opts['ntlm_transform_request'] && self.ntlm_client req = req.opts['ntlm_transform_request'].call(self.ntlm_client, req) elsif req.respond_to?(:opts) && req.opts['krb_transform_request'] && self.krb_encryptor req = req.opts['krb_transform_request'].call(self.krb_encryptor, req) end send_request(req, t) res = read_response(t, :original_request => req) if req.respond_to?(:opts) && req.opts['ntlm_transform_response'] && self.ntlm_client req.opts['ntlm_transform_response'].call(self.ntlm_client, res) elsif req.respond_to?(:opts) && req.opts['krb_transform_response'] && self.krb_encryptor req = req.opts['krb_transform_response'].call(self.krb_encryptor, res) end res.request = req.to_s if res res.peerinfo = peerinfo if res subscriber.on_response(res) res end |
#basic_auth_header(username, password) ⇒ String
Converts username and password into the HTTP Basic authorization string.
340 341 342 343 |
# File 'lib/rex/proto/http/client.rb', line 340 def basic_auth_header(username,password) auth_str = username.to_s + ":" + password.to_s auth_str = "Basic " + Rex::Text.encode_base64(auth_str) end |
#channel_binding ⇒ Object
617 618 619 620 621 622 623 |
# File 'lib/rex/proto/http/client.rb', line 617 def channel_binding if !self.conn.respond_to?(:peer_cert) or self.conn.peer_cert.nil? nil else Net::NTLM::ChannelBinding.create(OpenSSL::X509::Certificate.new(self.conn.peer_cert)) end end |
#close ⇒ Object
Closes the connection to the remote server.
198 199 200 201 202 203 204 205 206 |
# File 'lib/rex/proto/http/client.rb', line 198 def close if self.conn && !self.conn.closed? self.conn.shutdown self.conn.close end self.conn = nil self.ntlm_client = nil end |
#conn? ⇒ Boolean
Returns whether or not the conn is valid.
730 731 732 |
# File 'lib/rex/proto/http/client.rb', line 730 def conn? conn != nil end |
#connect(t = -1)) ⇒ Rex::Socket::Tcp
Connects to the remote server if possible.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/rex/proto/http/client.rb', line 168 def connect(t = -1) # If we already have a connection and we aren't pipelining, close it. if (self.conn) if !pipelining? close else return self.conn end end timeout = (t.nil? or t == -1) ? 0 : t self.conn = Rex::Socket::Tcp.create( 'PeerHost' => self.hostname, 'PeerHostname' => self.config['ssl_server_name_indication'] || self.config['vhost'], 'PeerPort' => self.port.to_i, 'LocalHost' => self.local_host, 'LocalPort' => self.local_port, 'Context' => self.context, 'SSL' => self.ssl, 'SSLVersion' => self.ssl_version, 'Proxies' => self.proxies, 'Timeout' => timeout, 'Comm' => self.comm ) end |
#digest_auth(opts = {}) ⇒ Response
Send a series of requests to complete Digest Authentication
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/rex/proto/http/client.rb', line 354 def digest_auth(opts={}) cnonce = make_cnonce nonce_count = 0 to = opts['timeout'] || 20 digest_user = opts['username'] || "" digest_password = opts['password'] || "" method = opts['method'] path = opts['uri'] iis = true if (opts['DigestAuthIIS'] == false or self.config['DigestAuthIIS'] == false) iis = false end begin nonce_count += 1 resp = opts['response'] if not resp # Get authentication-challenge from server, and read out parameters required r = request_cgi(opts.merge({ 'uri' => path, 'method' => method })) resp = _send_recv(r, to) unless resp.kind_of? Rex::Proto::Http::Response return nil end if resp.code != 401 return resp end return resp unless resp.headers['WWW-Authenticate'] end # Don't anchor this regex to the beginning of string because header # folding makes it appear later when the server presents multiple # WWW-Authentication options (such as is the case with IIS configured # for Digest or NTLM). resp['www-authenticate'] =~ /Digest (.*)/ parameters = {} $1.split(/,[[:space:]]*/).each do |p| k, v = p.split("=", 2) parameters[k] = v.gsub('"', '') end qop = parameters['qop'] if parameters['algorithm'] =~ /(.*?)(-sess)?$/ algorithm = case $1 when 'MD5' then Digest::MD5 when 'SHA1' then Digest::SHA1 when 'SHA2' then Digest::SHA2 when 'SHA256' then Digest::SHA256 when 'SHA384' then Digest::SHA384 when 'SHA512' then Digest::SHA512 when 'RMD160' then Digest::RMD160 else raise Error, "unknown algorithm \"#{$1}\"" end algstr = parameters["algorithm"] sess = $2 else algorithm = Digest::MD5 algstr = "MD5" sess = false end a1 = if sess then [ algorithm.hexdigest("#{digest_user}:#{parameters['realm']}:#{digest_password}"), parameters['nonce'], cnonce ].join ':' else "#{digest_user}:#{parameters['realm']}:#{digest_password}" end ha1 = algorithm.hexdigest(a1) ha2 = algorithm.hexdigest("#{method}:#{path}") request_digest = [ha1, parameters['nonce']] request_digest.push(('%08x' % nonce_count), cnonce, qop) if qop request_digest << ha2 request_digest = request_digest.join ':' # Same order as IE7 auth = [ "Digest username=\"#{digest_user}\"", "realm=\"#{parameters['realm']}\"", "nonce=\"#{parameters['nonce']}\"", "uri=\"#{path}\"", "cnonce=\"#{cnonce}\"", "nc=#{'%08x' % nonce_count}", "algorithm=#{algstr}", "response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"", # The spec says the qop value shouldn't be enclosed in quotes, but # some versions of IIS require it and Apache accepts it. Chrome # and Firefox both send it without quotes but IE does it this way. # Use the non-compliant-but-everybody-does-it to be as compatible # as possible by default. The user can override if they don't like # it. if qop.nil? then elsif iis then "qop=\"#{qop}\"" else "qop=#{qop}" end, if parameters.key? 'opaque' then "opaque=\"#{parameters['opaque']}\"" end ].compact headers ={ 'Authorization' => auth.join(', ') } headers.merge!(opts['headers']) if opts['headers'] # Send main request with authentication r = request_cgi(opts.merge({ 'uri' => path, 'method' => method, 'headers' => headers })) resp = _send_recv(r, to, true) unless resp.kind_of? Rex::Proto::Http::Response return nil end return resp rescue ::Errno::EPIPE, ::Timeout::Error end end |
#kerberos_auth(opts = {}) ⇒ Object
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
# File 'lib/rex/proto/http/client.rb', line 488 def kerberos_auth(opts={}) to = opts['timeout'] || 20 auth_result = self.kerberos_authenticator.authenticate(mechanism: Rex::Proto::Gss::Mechanism::KERBEROS) gss_data = auth_result[:security_blob] gss_data_b64 = Rex::Text.encode_base64(gss_data) # Separate options for the auth requests auth_opts = opts.clone auth_opts['headers'] = opts['headers'].clone auth_opts['headers']['Authorization'] = "Kerberos #{gss_data_b64}" if auth_opts['no_body_for_auth'] auth_opts.delete('data') auth_opts.delete('krb_transform_request') auth_opts.delete('krb_transform_response') end begin # Send the auth request r = request_cgi(auth_opts) resp = _send_recv(r, to) unless resp.kind_of? Rex::Proto::Http::Response return nil end # Get the challenge and craft the response response = resp.headers['WWW-Authenticate'].scan(/Kerberos ([A-Z0-9\x2b\x2f=]+)/ni).flatten[0] return resp unless response decoded = Rex::Text.decode_base64(response) mutual_auth_result = self.kerberos_authenticator.parse_gss_init_response(decoded, auth_result[:session_key], mechanism: 'kerberos') self.krb_encryptor = self.kerberos_authenticator.(mutual_auth_result[:ap_rep_subkey], auth_result[:client_sequence_number], mutual_auth_result[:server_sequence_number]) if opts['no_body_for_auth'] # If the body wasn't sent in the authentication, now do the actual request r = request_cgi(opts) resp = _send_recv(r, to, true) end return resp rescue ::Errno::EPIPE, ::Timeout::Error return nil end end |
#make_cnonce ⇒ Object
346 347 348 |
# File 'lib/rex/proto/http/client.rb', line 346 def make_cnonce Digest::MD5.hexdigest "%x" % (::Time.now.to_i + rand(65535)) end |
#negotiate_auth(opts = {}) ⇒ Response
Builds a series of requests to complete Negotiate Auth. Works essentially the same way as Digest auth. Same pipelining concerns exist.
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/rex/proto/http/client.rb', line 543 def negotiate_auth(opts={}) to = opts['timeout'] || 20 opts['username'] ||= '' opts['password'] ||= '' if opts['provider'] and opts['provider'].include? 'Negotiate' provider = "Negotiate " else provider = "NTLM " end opts['method']||= 'GET' opts['headers']||= {} workstation_name = Rex::Text.rand_text_alpha(rand(8)+6) domain_name = self.config['domain'] ntlm_client = ::Net::NTLM::Client.new( opts['username'], opts['password'], workstation: workstation_name, domain: domain_name, ) type1 = ntlm_client.init_context begin # Separate options for the auth requests auth_opts = opts.clone auth_opts['headers'] = opts['headers'].clone auth_opts['headers']['Authorization'] = provider + type1.encode64 if auth_opts['no_body_for_auth'] auth_opts.delete('data') auth_opts.delete('ntlm_transform_request') auth_opts.delete('ntlm_transform_response') end # First request to get the challenge r = request_cgi(auth_opts) resp = _send_recv(r, to) unless resp.kind_of? Rex::Proto::Http::Response return nil end return resp unless resp.code == 401 && resp.headers['WWW-Authenticate'] # Get the challenge and craft the response ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0] return resp unless ntlm_challenge = ntlm_client.init_context(ntlm_challenge, channel_binding) self.ntlm_client = ntlm_client # Send the response auth_opts['headers']['Authorization'] = "#{provider}#{.encode64}" r = request_cgi(auth_opts) resp = _send_recv(r, to, true) unless resp.kind_of? Rex::Proto::Http::Response return nil end if opts['no_body_for_auth'] # If the body wasn't sent in the authentication, now do the actual request r = request_cgi(opts) resp = _send_recv(r, to, true) end return resp rescue ::Errno::EPIPE, ::Timeout::Error return nil end end |
#peerinfo ⇒ Object
Target host addr and port for this connection
744 745 746 747 748 749 750 751 752 753 754 755 |
# File 'lib/rex/proto/http/client.rb', line 744 def peerinfo if self.conn pi = self.conn.peerinfo || nil if pi return { 'addr' => pi.split(':')[0], 'port' => pi.split(':')[1].to_i } end end nil end |
#pipelining? ⇒ Boolean
Whether or not connections should be pipelined.
737 738 739 |
# File 'lib/rex/proto/http/client.rb', line 737 def pipelining? pipeline end |
#read_response(t = -1,, opts = {}) ⇒ Response
Read a response from the server
Wait at most t seconds for the full response to be read in. If t is specified as a negative value, it indicates an indefinite wait cycle. If t is specified as nil or 0, it indicates no response parsing is required.
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 |
# File 'lib/rex/proto/http/client.rb', line 632 def read_response(t = -1, opts = {}) # Return a nil response if timeout is nil or 0 return if t.nil? || t == 0 resp = Response.new resp.max_data = config['read_max_data'] original_request = opts.fetch(:original_request) { nil } parse_opts = {} unless original_request.nil? parse_opts = { :orig_method => original_request.opts['method'] } end Timeout.timeout((t < 0) ? nil : t) do rv = nil while ( not conn.closed? and rv != Packet::ParseCode::Completed and rv != Packet::ParseCode::Error ) begin buff = conn.get_once(resp.max_data, 1) rv = resp.parse(buff || '', parse_opts) # Handle unexpected disconnects rescue ::Errno::EPIPE, ::EOFError, ::IOError case resp.state when Packet::ParseState::ProcessingHeader resp = nil when Packet::ParseState::ProcessingBody # truncated request, good enough resp.error = :truncated end break end # This is a dirty hack for broken HTTP servers if rv == Packet::ParseCode::Completed rbody = resp.body rbufq = resp.bufq rblob = rbody.to_s + rbufq.to_s tries = 0 begin # XXX: This doesn't deal with chunked encoding while tries < 1000 and resp.headers["Content-Type"] and resp.headers["Content-Type"].start_with?('text/html') and rblob !~ /<\/html>/i buff = conn.get_once(-1, 0.05) break if not buff rblob += buff tries += 1 end rescue ::Errno::EPIPE, ::EOFError, ::IOError end resp.bufq = "" resp.body = rblob end end end return resp if not resp # As a last minute hack, we check to see if we're dealing with a 100 Continue here. # Most of the time this is handled by the parser via check_100() if resp.proto == '1.1' and resp.code == 100 and not opts[:skip_100] # Read the real response from the body if we found one # If so, our real response became the body, so we re-parse it. if resp.body.to_s =~ /^HTTP/ body = resp.body resp = Response.new resp.max_data = config['read_max_data'] rv = resp.parse(body, parse_opts) # We found a 100 Continue but didn't read the real reply yet # Otherwise reread the reply, but don't try this hack again else resp = read_response(t, :skip_100 => true) end end resp rescue Timeout::Error # Allow partial response due to timeout resp if config['partial'] end |
#request_cgi(opts = {}) ⇒ ClientRequest
Create a CGI compatible request
152 153 154 155 156 157 158 159 160 |
# File 'lib/rex/proto/http/client.rb', line 152 def request_cgi(opts = {}) opts = self.config.merge(opts) opts['cgi'] = true opts['port'] = self.port opts['ssl'] = self.ssl ClientRequest.new(opts) end |
#request_raw(opts = {}) ⇒ ClientRequest
Create an arbitrary HTTP request
130 131 132 133 134 135 136 137 138 |
# File 'lib/rex/proto/http/client.rb', line 130 def request_raw(opts = {}) opts = self.config.merge(opts) opts['cgi'] = false opts['port'] = self.port opts['ssl'] = self.ssl ClientRequest.new(opts) end |
#send_auth(res, opts, t, persist) ⇒ Response
Resends an HTTP Request with the propper authentcation headers set. If we do not support the authentication type the server requires we return the original response object
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 329 330 331 332 333 334 |
# File 'lib/rex/proto/http/client.rb', line 278 def send_auth(res, opts, t, persist) if opts['username'].nil? or opts['username'] == '' if self.username and not (self.username == '') opts['username'] = self.username opts['password'] = self.password else opts['username'] = nil opts['password'] = nil end end if opts[:kerberos_authenticator].nil? opts[:kerberos_authenticator] = self.kerberos_authenticator end return res if (opts['username'].nil? or opts['username'] == '') and opts[:kerberos_authenticator].nil? supported_auths = res.headers['WWW-Authenticate'] # if several providers are available, the client may want one in particular preferred_auth = opts['preferred_auth'] if supported_auths.include?('Basic') && (preferred_auth.nil? || preferred_auth == 'Basic') opts['headers'] ||= {} opts['headers']['Authorization'] = basic_auth_header(opts['username'],opts['password'] ) req = request_cgi(opts) res = _send_recv(req,t,persist) return res elsif supported_auths.include?('Digest') && (preferred_auth.nil? || preferred_auth == 'Digest') temp_response = digest_auth(opts) if temp_response.kind_of? Rex::Proto::Http::Response res = temp_response end return res elsif supported_auths.include?('NTLM') && (preferred_auth.nil? || preferred_auth == 'NTLM') opts['provider'] = 'NTLM' temp_response = negotiate_auth(opts) if temp_response.kind_of? Rex::Proto::Http::Response res = temp_response end return res elsif supported_auths.include?('Negotiate') && (preferred_auth.nil? || preferred_auth == 'Negotiate') opts['provider'] = 'Negotiate' temp_response = negotiate_auth(opts) if temp_response.kind_of? Rex::Proto::Http::Response res = temp_response end return res elsif supported_auths.include?('Negotiate') && (preferred_auth.nil? || preferred_auth == 'Kerberos') opts['provider'] = 'Negotiate' temp_response = kerberos_auth(opts) if temp_response.kind_of? Rex::Proto::Http::Response res = temp_response end return res end return res end |
#send_recv(req, t = -1,, persist = false) ⇒ Response
Sends a request and gets a response back
If the request is a 401, and we have creds, it will attempt to complete authentication and return the final response
215 216 217 218 219 220 221 |
# File 'lib/rex/proto/http/client.rb', line 215 def send_recv(req, t = -1, persist = false) res = _send_recv(req, t, persist) if res and res.code == 401 and res.headers['WWW-Authenticate'] res = send_auth(res, req.opts, t, persist) end res end |
#send_request(req, t = -1)) ⇒ void
This method returns an undefined value.
Send an HTTP request to the server
263 264 265 266 |
# File 'lib/rex/proto/http/client.rb', line 263 def send_request(req, t = -1) connect(t) conn.put(req.to_s) end |
#set_config(opts = {}) ⇒ Object
Set configuration options
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/rex/proto/http/client.rb', line 83 def set_config(opts = {}) opts.each_pair do |var,val| # Default type is string typ = self.config_types[var] || 'string' # These are enum types if typ.is_a?(Array) if not typ.include?(val) raise RuntimeError, "The specified value for #{var} is not one of the valid choices" end end # The caller should have converted these to proper ruby types, but # take care of the case where they didn't before setting the # config. if(typ == 'bool') val = (val == true || val.to_s =~ /^(t|y|1)/i) end if(typ == 'integer') val = val.to_i end self.config[var]=val end end |
#stop ⇒ Object
Cleans up any outstanding connections and other resources.
723 724 725 |
# File 'lib/rex/proto/http/client.rb', line 723 def stop close end |