net-tns 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +202 -0
- data/bin/tns_oradb_version.rb +15 -0
- data/bin/tns_sid_enumeration.rb +52 -0
- data/bin/tns_sid_list.rb +43 -0
- data/lib/net/tns/client.rb +94 -0
- data/lib/net/tns/connection.rb +201 -0
- data/lib/net/tns/exceptions.rb +34 -0
- data/lib/net/tns/gem_version.rb +5 -0
- data/lib/net/tns/helpers/string_helpers.rb +49 -0
- data/lib/net/tns/packet.rb +82 -0
- data/lib/net/tns/packets/abort_packet.rb +12 -0
- data/lib/net/tns/packets/accept_packet.rb +19 -0
- data/lib/net/tns/packets/ack_packet.rb +7 -0
- data/lib/net/tns/packets/attention_packet.rb +9 -0
- data/lib/net/tns/packets/connect_packet.rb +55 -0
- data/lib/net/tns/packets/control_packet.rb +7 -0
- data/lib/net/tns/packets/data_packet.rb +20 -0
- data/lib/net/tns/packets/marker_packet.rb +18 -0
- data/lib/net/tns/packets/null_packet.rb +7 -0
- data/lib/net/tns/packets/redirect_packet.rb +10 -0
- data/lib/net/tns/packets/refuse_packet.rb +12 -0
- data/lib/net/tns/packets/resend_packet.rb +7 -0
- data/lib/net/tns/version.rb +15 -0
- data/lib/net/tns.rb +20 -0
- data/lib/net/tti/client.rb +90 -0
- data/lib/net/tti/connection.rb +142 -0
- data/lib/net/tti/crypto.rb +189 -0
- data/lib/net/tti/data_types/chunked_string.rb +63 -0
- data/lib/net/tti/data_types/key_value_pair.rb +25 -0
- data/lib/net/tti/data_types.rb +2 -0
- data/lib/net/tti/exceptions.rb +62 -0
- data/lib/net/tti/message.rb +50 -0
- data/lib/net/tti/messages/data_type_negotiation_request.rb +133 -0
- data/lib/net/tti/messages/data_type_negotiation_response.rb +9 -0
- data/lib/net/tti/messages/error_message.rb +14 -0
- data/lib/net/tti/messages/function_call.rb +46 -0
- data/lib/net/tti/messages/function_calls/authentication.rb +45 -0
- data/lib/net/tti/messages/function_calls/authentication_x64.rb +42 -0
- data/lib/net/tti/messages/function_calls/authentication_x86.rb +53 -0
- data/lib/net/tti/messages/function_calls/pre_authentication_response.rb +32 -0
- data/lib/net/tti/messages/protocol_negotiation_request.rb +29 -0
- data/lib/net/tti/messages/protocol_negotiation_response.rb +44 -0
- data/lib/net/tti.rb +18 -0
- metadata +128 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module Net::TNS
|
2
|
+
# This module includes common string helper methods for monkey-patching
|
3
|
+
# or mixing-in to string objects.
|
4
|
+
module StringHelpers
|
5
|
+
HEXCHARS = [("0".."9").to_a, ("a".."f").to_a].flatten
|
6
|
+
|
7
|
+
#From the Ruby Black Bag (http://github.com/emonti/rbkb/)
|
8
|
+
# Convert a string to ASCII hex string. Supports a few options for format:
|
9
|
+
#
|
10
|
+
# :delim - delimter between each hex byte
|
11
|
+
# :prefix - prefix before each hex byte
|
12
|
+
# :suffix - suffix after each hex byte
|
13
|
+
#
|
14
|
+
def tns_hexify(opts={})
|
15
|
+
delim = opts[:delim]
|
16
|
+
pre = (opts[:prefix] || "")
|
17
|
+
suf = (opts[:suffix] || "")
|
18
|
+
|
19
|
+
if (rx=opts[:rx]) and not rx.kind_of? Regexp
|
20
|
+
raise "rx must be a regular expression for a character class"
|
21
|
+
end
|
22
|
+
|
23
|
+
out=Array.new
|
24
|
+
|
25
|
+
self.each_byte do |c|
|
26
|
+
hc = if (rx and not rx.match c.chr)
|
27
|
+
c.chr
|
28
|
+
else
|
29
|
+
pre + (HEXCHARS[(c >> 4)] + HEXCHARS[(c & 0xf )]) + suf
|
30
|
+
end
|
31
|
+
out << (hc)
|
32
|
+
end
|
33
|
+
out.join(delim)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Convert ASCII hex string to raw.
|
37
|
+
#
|
38
|
+
# Parameters:
|
39
|
+
#
|
40
|
+
# d = optional 'delimiter' between hex bytes (zero+ spaces by default)
|
41
|
+
def tns_unhexify(d=/\s*/)
|
42
|
+
self.strip.gsub(/([A-Fa-f0-9]{1,2})#{d}?/) { $1.hex.chr }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class String
|
48
|
+
include Net::TNS::StringHelpers
|
49
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "bindata"
|
2
|
+
|
3
|
+
module Net
|
4
|
+
module TNS
|
5
|
+
class Header < BinData::Record
|
6
|
+
LENGTH = 8
|
7
|
+
|
8
|
+
uint16be :packet_length
|
9
|
+
uint16be :packet_checksum
|
10
|
+
uint8 :packet_type
|
11
|
+
uint8 :flags
|
12
|
+
uint16be :checksum
|
13
|
+
end
|
14
|
+
|
15
|
+
class Packet < BinData::Record
|
16
|
+
SESSION_DATA_UNIT_SIZE = 2048
|
17
|
+
MAX_PAYLOAD_SIZE = SESSION_DATA_UNIT_SIZE - Header::LENGTH
|
18
|
+
|
19
|
+
# BinData fields
|
20
|
+
header :header
|
21
|
+
|
22
|
+
|
23
|
+
def self.register_tns_type(tns_type)
|
24
|
+
@@tns_packet_classes ||= {}
|
25
|
+
@@tns_packet_types ||= {}
|
26
|
+
if @@tns_packet_classes.has_key?(tns_type)
|
27
|
+
existing_class = @@tns_packet_classes[tns_type]
|
28
|
+
raise("Duplicate TNS Types Defined: #{existing_class} and #{self} both have a type of #{tns_type}")
|
29
|
+
end
|
30
|
+
|
31
|
+
@@tns_packet_classes[tns_type] = self
|
32
|
+
@@tns_packet_types[self] = tns_type
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.from_socket( socket )
|
37
|
+
Net::TNS.logger.debug("Attempting to read header")
|
38
|
+
# TODO: allow sockets to implement their own timeout behavior
|
39
|
+
require "timeout"
|
40
|
+
begin
|
41
|
+
header_raw = Timeout::timeout(5) do
|
42
|
+
socket.read(Header::LENGTH)
|
43
|
+
end
|
44
|
+
rescue Timeout::Error
|
45
|
+
raise Exceptions::ReceiveTimeoutExceeded
|
46
|
+
end
|
47
|
+
|
48
|
+
if header_raw.nil? || header_raw.length != Header::LENGTH
|
49
|
+
raise Exceptions::ProtocolException
|
50
|
+
end
|
51
|
+
|
52
|
+
header = Header.new()
|
53
|
+
header.read( header_raw )
|
54
|
+
Net::TNS.logger.debug("Read header. Reported packet length is #{header.packet_length} bytes")
|
55
|
+
raise Exceptions::ProtocolException if header.packet_length > SESSION_DATA_UNIT_SIZE
|
56
|
+
|
57
|
+
payload_raw = socket.read( header.packet_length - Header::LENGTH )
|
58
|
+
packet_raw = header_raw + payload_raw
|
59
|
+
|
60
|
+
unless payload_class = @@tns_packet_classes[ header.packet_type ]
|
61
|
+
raise Net::TNS::Exceptions::TnsException.new( "Unknown TNS packet type: #{header.packet_type}" )
|
62
|
+
end
|
63
|
+
|
64
|
+
new_packet = payload_class.read( packet_raw )
|
65
|
+
return new_packet
|
66
|
+
end
|
67
|
+
|
68
|
+
def update_header()
|
69
|
+
self.header.packet_type = @@tns_packet_types[self.class]
|
70
|
+
self.header.packet_length = self.num_bytes
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_binary_s()
|
74
|
+
update_header()
|
75
|
+
return super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
require "pathname"
|
82
|
+
Dir.glob("#{Pathname.new(__FILE__).dirname}/packets/*.rb") { |file| require file }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Net
|
2
|
+
module TNS
|
3
|
+
class AcceptPacket < Packet
|
4
|
+
register_tns_type 2
|
5
|
+
|
6
|
+
uint16be :version
|
7
|
+
uint16be :service_flags
|
8
|
+
uint16be :sdu_size # session data unit size
|
9
|
+
uint16be :maximum_tdu_size # maximum transmission data unit size
|
10
|
+
uint16be :byte_order # 0x0001 for little endian 0x0100 for big endian
|
11
|
+
uint16be :data_length
|
12
|
+
uint16be :data_offset # Offset to Connect Data (from parent header start)
|
13
|
+
uint8 :flags1
|
14
|
+
uint8 :flags2
|
15
|
+
string :padding, :read_length => lambda {data_offset - 24}
|
16
|
+
string :data, :read_length => :data_length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "net/tns/version"
|
2
|
+
|
3
|
+
module Net
|
4
|
+
module TNS
|
5
|
+
class ConnectPacket < Packet
|
6
|
+
register_tns_type 1
|
7
|
+
|
8
|
+
uint16be :maximum_version, :initial_value => Net::TNS::Version::VERSION_11G
|
9
|
+
uint16be :minimum_version, :initial_value => Net::TNS::Version::ALL_VERSIONS.min
|
10
|
+
uint16be :service_flags
|
11
|
+
# session data unit size
|
12
|
+
uint16be :sdu_size, :initial_value => 0x0800
|
13
|
+
# maximum transmission data unit size
|
14
|
+
uint16be :maximum_tdu_size, :initial_value => 0x7fff
|
15
|
+
uint16be :protocol_flags, :initial_value => 0xc608
|
16
|
+
# Described as "Max packets before ACK" by http://www.pythian.com/blog/repost-oracle-protocol/
|
17
|
+
uint16be :line_turnaround_value
|
18
|
+
# 0x0001 for little endian 0x0100 for big endian
|
19
|
+
uint16be :byte_order, :initial_value => 0x0001
|
20
|
+
uint16be :data_length, :value => lambda { data.length } # Length of Connect Data
|
21
|
+
uint16be :data_offset, :value => lambda { supports_trace? ? 58 : 34 } # Offset to Connect Data (from start of TNS header)
|
22
|
+
# Maximum Receivable Connect Data
|
23
|
+
uint32be :maximum_connect_receive
|
24
|
+
uint8 :flags1, :initial_value => 0x41
|
25
|
+
uint8 :flags2, :initial_value => 0x41
|
26
|
+
|
27
|
+
uint32be :trace_item1, :onlyif => :supports_trace?
|
28
|
+
uint32be :trace_item2, :onlyif => :supports_trace?
|
29
|
+
uint64be :trace_connection_id, :onlyif => :supports_trace?
|
30
|
+
uint64be :unknown, :onlyif => :supports_trace?
|
31
|
+
|
32
|
+
string :data
|
33
|
+
|
34
|
+
def self.make_connect_request(dst_host, dst_port, target_clause)
|
35
|
+
conn_info = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%i))(CONNECT_DATA=(SERVER=DEDICATED)%s))" %
|
36
|
+
[ dst_host, dst_port, target_clause ]
|
37
|
+
return self.new(:data => conn_info)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.make_connection_by_sid(dst_host, dst_port, sid)
|
41
|
+
target_clause = "(SID=#{sid})"
|
42
|
+
return make_connect_request(dst_host, dst_port, target_clause)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.make_connection_by_service_name(dst_host, dst_port, service_name)
|
46
|
+
target_clause = "(SERVICE_NAME=#{service_name})"
|
47
|
+
return make_connect_request(dst_host, dst_port, target_clause)
|
48
|
+
end
|
49
|
+
|
50
|
+
def supports_trace?
|
51
|
+
return maximum_version > 308
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Net
|
2
|
+
module TNS
|
3
|
+
class DataPacket < Packet
|
4
|
+
register_tns_type 6
|
5
|
+
|
6
|
+
uint16be :flags
|
7
|
+
rest :data
|
8
|
+
|
9
|
+
def self.max_data_length
|
10
|
+
return Net::TNS::Packet::MAX_PAYLOAD_SIZE - 2 # 2 = flags.length
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.make_disconnect_request
|
14
|
+
packet = self.new()
|
15
|
+
packet.flags = 0x0040
|
16
|
+
return packet
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Net
|
2
|
+
module TNS
|
3
|
+
class MarkerPacket < Packet
|
4
|
+
register_tns_type 12
|
5
|
+
|
6
|
+
uint8 :marker_type
|
7
|
+
rest :data
|
8
|
+
|
9
|
+
def self.create_request
|
10
|
+
request = self.new
|
11
|
+
request.marker_type = 1
|
12
|
+
request.data = "0002".tns_unhexify
|
13
|
+
|
14
|
+
return request
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
module Net
|
3
|
+
module TNS
|
4
|
+
module Version
|
5
|
+
VERSION_9I = 312
|
6
|
+
VERSION_10G = 313
|
7
|
+
VERSION_11G = 314
|
8
|
+
# Other important versions include:
|
9
|
+
# 308 - The last version before some significant changes to TNS, particularly noticeable in Connect packets
|
10
|
+
# 300 - Many Oracle clients send this as their minimum version
|
11
|
+
|
12
|
+
ALL_VERSIONS = [ VERSION_9I, VERSION_10G, VERSION_11G ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/net/tns.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Net
|
2
|
+
module TNS
|
3
|
+
require "net/tns/packet"
|
4
|
+
require "net/tns/exceptions"
|
5
|
+
require "net/tns/client"
|
6
|
+
require "net/tns/connection"
|
7
|
+
require "net/tns/gem_version"
|
8
|
+
require "net/tns/helpers/string_helpers"
|
9
|
+
|
10
|
+
def self.logger
|
11
|
+
unless defined?(@@logger)
|
12
|
+
require "logger"
|
13
|
+
@@logger = Logger.new(STDERR)
|
14
|
+
@@logger.progname = "Net::TNS"
|
15
|
+
@@logger.sev_threshold = 6 unless $DEBUG
|
16
|
+
end
|
17
|
+
return @@logger
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "net/tti"
|
2
|
+
require "net/tns"
|
3
|
+
|
4
|
+
module Net
|
5
|
+
module TTI
|
6
|
+
class Client
|
7
|
+
def connect(opts={})
|
8
|
+
socket_opts = {}
|
9
|
+
socket_opts_keys = [:host, :port]
|
10
|
+
socket_opts_keys.each {|key| socket_opts[key] = opts.delete(key) if opts.has_key?(key)}
|
11
|
+
|
12
|
+
connect_opts = {}
|
13
|
+
connect_opts_keys = [:sid, :service_name]
|
14
|
+
connect_opts_keys.each {|key| connect_opts[key] = opts.delete(key) if opts.has_key?(key)}
|
15
|
+
|
16
|
+
unless opts.empty?
|
17
|
+
raise ArgumentError.new("Unrecognized #connect options: #{opts.keys.join(",")}")
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
@tti_conn = Net::TTI::Connection.new(socket_opts)
|
22
|
+
@tti_conn.connect(connect_opts)
|
23
|
+
rescue
|
24
|
+
(@tti_conn.disconnect rescue nil) unless @tti_conn.nil?
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
Net::TTI.logger.info("TTI connection established. #{@tti_conn.conn_params}")
|
28
|
+
end
|
29
|
+
|
30
|
+
def authenticate( username, password )
|
31
|
+
pre_auth_request = get_pre_auth_request( username )
|
32
|
+
|
33
|
+
pre_auth_response_raw = @tti_conn.send_and_receive(pre_auth_request)
|
34
|
+
pre_auth_response = PreAuthenticationResponse.read(pre_auth_response_raw)
|
35
|
+
|
36
|
+
auth_request = get_auth_request(username, password, pre_auth_response)
|
37
|
+
|
38
|
+
begin
|
39
|
+
@tti_conn.send_and_receive(auth_request)
|
40
|
+
rescue Exceptions::ErrorMessageReceived => error
|
41
|
+
case error.error_code
|
42
|
+
when 1017, 9275
|
43
|
+
raise Exceptions::InvalidCredentialsError.new( error.message )
|
44
|
+
when 28000
|
45
|
+
raise Exceptions::AccountLockedOutError.new( error.message )
|
46
|
+
when 28001
|
47
|
+
raise Exceptions::PasswordExpiredError.new( error.message )
|
48
|
+
when 28002
|
49
|
+
# This is "password will expire in 7 days," i.e. a successful login
|
50
|
+
else
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_pre_auth_request(username)
|
59
|
+
pre_auth_request = Authentication.create_pre_auth_request( @tti_conn.conn_params.architecture )
|
60
|
+
pre_auth_request.username = username
|
61
|
+
return pre_auth_request
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_auth_request(username, password, pre_auth_response)
|
65
|
+
auth_sesskey = pre_auth_response.auth_sesskey
|
66
|
+
|
67
|
+
case @tti_conn.conn_params.tns_version
|
68
|
+
when Net::TNS::Version::VERSION_10G
|
69
|
+
enc_password, enc_client_session_key = Net::TTI::Crypto.get_10g_auth_values( username, password, auth_sesskey )
|
70
|
+
when Net::TNS::Version::VERSION_11G
|
71
|
+
auth_vfr_data = pre_auth_response.auth_vfr_data
|
72
|
+
enc_password, enc_client_session_key = Net::TTI::Crypto.get_11g_auth_values( password, auth_sesskey, auth_vfr_data )
|
73
|
+
else
|
74
|
+
raise Exceptions::UnsupportedTNSVersion.new( @tti_conn.conn_params.tns_version )
|
75
|
+
end
|
76
|
+
|
77
|
+
auth_request = Authentication.create_auth_request( @tti_conn.conn_params.architecture )
|
78
|
+
auth_request.username = username
|
79
|
+
auth_request.enc_client_session_key = enc_client_session_key
|
80
|
+
auth_request.enc_password = enc_password
|
81
|
+
|
82
|
+
return auth_request
|
83
|
+
end
|
84
|
+
|
85
|
+
def disconnect
|
86
|
+
@tti_conn.disconnect() unless @tti_conn.nil?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "net/tns"
|
2
|
+
require "net/tti/message"
|
3
|
+
|
4
|
+
module Net
|
5
|
+
module TTI
|
6
|
+
class ConnectionParameters
|
7
|
+
attr_accessor :platform
|
8
|
+
attr_accessor :architecture
|
9
|
+
attr_accessor :ttc_version
|
10
|
+
attr_accessor :tns_version
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
return "Platform: #{@platform||nil}" +
|
14
|
+
"; Architecture: #{@architecture||nil}" +
|
15
|
+
"; TTC? Version: #{@ttc_version||nil}" +
|
16
|
+
"; TNS Version: #{@tns_version||nil}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Connection
|
21
|
+
attr_reader :conn_params
|
22
|
+
|
23
|
+
def initialize(opts={})
|
24
|
+
Net::TTI.logger.debug("Creating new TNS Connection")
|
25
|
+
@tns_connection = Net::TNS::Connection.new(opts)
|
26
|
+
@conn_params = ConnectionParameters.new()
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect(opts={})
|
30
|
+
Net::TTI.logger.debug("Connection#connect called")
|
31
|
+
@tns_connection.connect(opts)
|
32
|
+
@conn_params.tns_version = @tns_connection.tns_protocol_version
|
33
|
+
|
34
|
+
Net::TTI.logger.debug("Sending protocol negotiation request")
|
35
|
+
proto_nego_request = ProtocolNegotiationRequest.create_request()
|
36
|
+
proto_nego_response_raw = send_and_receive( proto_nego_request )
|
37
|
+
|
38
|
+
proto_nego_response = ProtocolNegotiationResponse.read( proto_nego_response_raw )
|
39
|
+
proto_nego_response.populate_connection_parameters( @conn_params )
|
40
|
+
|
41
|
+
Net::TTI.logger.debug("Sending data type negotiation request")
|
42
|
+
dt_nego_request = DataTypeNegotiationRequest.create_request( @conn_params.platform )
|
43
|
+
dt_nego_response_raw = send_and_receive( dt_nego_request )
|
44
|
+
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def disconnect()
|
49
|
+
@tns_connection.disconnect
|
50
|
+
end
|
51
|
+
|
52
|
+
def send_and_receive( tti_message )
|
53
|
+
send_tti_message(tti_message)
|
54
|
+
receive_tti_message()
|
55
|
+
end
|
56
|
+
|
57
|
+
# Sends a TTI message. This function takes a TTI payload, embeds it in one
|
58
|
+
# or more TNS Data packets and sends those packets.
|
59
|
+
def send_tti_message( tti_message )
|
60
|
+
raw_message = tti_message.to_binary_s
|
61
|
+
Net::TTI.logger.debug( "Connection#send_tti_message called with #{raw_message.length}-byte #{tti_message.class} message" )
|
62
|
+
|
63
|
+
# Split the message into multiple packets if necessary
|
64
|
+
max_data = Net::TNS::DataPacket.max_data_length
|
65
|
+
raw_message.scan(/.{1,#{max_data}}/m).each do |raw_message_part|
|
66
|
+
tns_packet = Net::TNS::DataPacket.new()
|
67
|
+
tns_packet.data = raw_message_part
|
68
|
+
Net::TTI.logger.debug( "Sending data packet (#{tns_packet.num_bytes} bytes total)" )
|
69
|
+
@tns_connection.send_tns_packet( tns_packet )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def receive_tti_message( waiting_for_error_message = false, max_message_length=10_000 )
|
74
|
+
# This is structured as a loop in order to handle messages (e.g. Markers)
|
75
|
+
# that need to be handled without returning to the caller. To keep a malicious
|
76
|
+
# server from making us loop continuously, we set an arbitrary limit of
|
77
|
+
# 10 loops without a "real" message before we throw an exception.
|
78
|
+
receive_count = 0
|
79
|
+
while ( true )
|
80
|
+
receive_count += 1
|
81
|
+
if ( receive_count >= 3 )
|
82
|
+
raise Exceptions::TnsException.new( "Maximum receive attempts exceeded - too many Markers received." )
|
83
|
+
end
|
84
|
+
|
85
|
+
Net::TTI.logger.debug("Attempting to receive packet (try ##{receive_count})")
|
86
|
+
case tns_packet = @tns_connection.receive_tns_packet()
|
87
|
+
when Net::TNS::DataPacket
|
88
|
+
message_data = tns_packet.data
|
89
|
+
# If this is a long packet, the data may have hit the max length and
|
90
|
+
# carried into an additional packet. We wouldn't need to do this if
|
91
|
+
# we could fully parse every message (or at least know lengths to
|
92
|
+
# read). I'm looking at you, DataTypeNegotiationResponse.
|
93
|
+
if tns_packet.num_bytes > 2000
|
94
|
+
begin
|
95
|
+
max_message_length -= message_data.length
|
96
|
+
message_data += receive_tti_message(waiting_for_error_message, max_message_length)
|
97
|
+
rescue Net::TNS::Exceptions::ReceiveTimeoutExceeded
|
98
|
+
Net::TTI.logger.debug("Hit receive timeout trying to read another Data packet")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
return message_data
|
103
|
+
|
104
|
+
# We received an error notification
|
105
|
+
when Net::TNS::MarkerPacket
|
106
|
+
Net::TTI.logger.info("Received MarkerPacket")
|
107
|
+
# TNS Markers seem to come in pairs. If we've already got one and sent
|
108
|
+
# the request for the error message, we'll ignore subsequent markers
|
109
|
+
# until we get the error message.
|
110
|
+
unless waiting_for_error_message
|
111
|
+
error_message = get_error_message()
|
112
|
+
raise Exceptions::ErrorMessageReceived.new( error_message )
|
113
|
+
end
|
114
|
+
|
115
|
+
# We received something else
|
116
|
+
else
|
117
|
+
Net::TTI.logger.warn("Received #{tns_packet.class} instead of Data")
|
118
|
+
if waiting_for_error_message
|
119
|
+
raise Exceptions::ProtocolException.new( "Invalid response while waiting for error message - got #{tns_packet.class}" )
|
120
|
+
else
|
121
|
+
raise Exceptions::ProtocolException.new( "Received #{tns_packet.class} instead of TNS data packet (for TTI)" )
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_error_message
|
128
|
+
error_request = Net::TNS::MarkerPacket.create_request()
|
129
|
+
@tns_connection.send_tns_packet( error_request )
|
130
|
+
|
131
|
+
raw_response = receive_tti_message(true)
|
132
|
+
response = Message.from_data_string(raw_response)
|
133
|
+
|
134
|
+
unless response.is_a?(ErrorMessage)
|
135
|
+
raise Exceptions::ProtocolException.new( "Received #{response.class} instead of error message" )
|
136
|
+
end
|
137
|
+
|
138
|
+
return response.message
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|