Files
metasploit-gs/lib/postgres/postgres-pr/connection.rb
T
Tod Beardsley a65af9c8b3 See #730. Forking and adding Postgres-PR, with the following changes:
Namespaced everything under Msf::Db::PostgreSQL, renamed top-level include to postgres_msf to disambiguate.
Included recursive requires for all files.
Noted the IO monkeypatch -- should revisit.
Added a testcase for database connections.

The reason for the namespacing is to avoid stomping on any existing Postgres-PR installations, or any other requires named "postgres" or "postgresql" or even "pg," since these may or may not support the method's we're using here. The seperate namespace also allows for easier integration of custom commands later on.




git-svn-id: file:///home/svn/framework3/trunk@8342 4d416f70-5f16-0410-b530-b9f4589650da
2010-02-01 19:49:36 +00:00

183 lines
4.2 KiB
Ruby

#
# Author:: Michael Neumann
# Copyright:: (c) 2005 by Michael Neumann
# License:: Same as Ruby's or BSD
#
require 'postgres_msf'
require 'postgres/postgres-pr/message'
require 'postgres/postgres-pr/version'
require 'uri'
require 'socket'
# Namespace for Metasploit branch.
module Msf
module Db
module PostgresPR
PROTO_VERSION = 3 << 16 #196608
class Connection
# A block which is called with the NoticeResponse object as parameter.
attr_accessor :notice_processor
#
# Returns one of the following statuses:
#
# PQTRANS_IDLE = 0 (connection idle)
# PQTRANS_INTRANS = 2 (idle, within transaction block)
# PQTRANS_INERROR = 3 (idle, within failed transaction)
# PQTRANS_UNKNOWN = 4 (cannot determine status)
#
# Not yet implemented is:
#
# PQTRANS_ACTIVE = 1 (command in progress)
#
def transaction_status
case @transaction_status
when ?I
0
when ?T
2
when ?E
3
else
4
end
end
def initialize(database, user, password=nil, uri = nil)
uri ||= DEFAULT_URI
@transaction_status = nil
@params = {}
establish_connection(uri)
@conn << StartupMessage.new(PROTO_VERSION, 'user' => user, 'database' => database).dump
loop do
msg = Message.read(@conn)
case msg
when AuthentificationClearTextPassword
raise ArgumentError, "no password specified" if password.nil?
@conn << PasswordMessage.new(password).dump
when AuthentificationCryptPassword
raise ArgumentError, "no password specified" if password.nil?
@conn << PasswordMessage.new(password.crypt(msg.salt)).dump
when AuthentificationMD5Password
raise ArgumentError, "no password specified" if password.nil?
require 'digest/md5'
m = Digest::MD5.hexdigest(password + user)
m = Digest::MD5.hexdigest(m + msg.salt)
m = 'md5' + m
@conn << PasswordMessage.new(m).dump
when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential
raise "unsupported authentification"
when AuthentificationOk
when ErrorResponse
raise msg.field_values.join("\t")
when NoticeResponse
@notice_processor.call(msg) if @notice_processor
when ParameterStatus
@params[msg.key] = msg.value
when BackendKeyData
# TODO
#p msg
when ReadyForQuery
@transaction_status = msg.backend_transaction_status_indicator
break
else
raise "unhandled message type"
end
end
end
def close
raise "connection already closed" if @conn.nil?
@conn.shutdown
@conn = nil
end
class Result
attr_accessor :rows, :fields, :cmd_tag
def initialize(rows=[], fields=[])
@rows, @fields = rows, fields
end
end
def query(sql)
@conn << Query.dump(sql)
result = Result.new
errors = []
loop do
msg = Message.read(@conn)
case msg
when DataRow
result.rows << msg.columns
when CommandComplete
result.cmd_tag = msg.cmd_tag
when ReadyForQuery
@transaction_status = msg.backend_transaction_status_indicator
break
when RowDescription
result.fields = msg.fields
when CopyInResponse
when CopyOutResponse
when EmptyQueryResponse
when ErrorResponse
# TODO
errors << msg
when NoticeResponse
@notice_processor.call(msg) if @notice_processor
else
# TODO
end
end
raise errors.map{|e| e.field_values.join("\t") }.join("\n") unless errors.empty?
result
end
DEFAULT_PORT = 5432
DEFAULT_HOST = 'localhost'
DEFAULT_PATH = '/tmp'
DEFAULT_URI =
if RUBY_PLATFORM.include?('win')
'tcp://' + DEFAULT_HOST + ':' + DEFAULT_PORT.to_s
else
'unix:' + File.join(DEFAULT_PATH, '.s.PGSQL.' + DEFAULT_PORT.to_s)
end
private
# tcp://localhost:5432
# unix:/tmp/.s.PGSQL.5432
def establish_connection(uri)
u = URI.parse(uri)
case u.scheme
when 'tcp'
@conn = TCPSocket.new(u.host || DEFAULT_HOST, u.port || DEFAULT_PORT)
when 'unix'
@conn = UNIXSocket.new(u.path)
else
raise 'unrecognized uri scheme format (must be tcp or unix)'
end
end
end
end # module PostgresPR
end
end