net-tns 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +202 -0
  3. data/bin/tns_oradb_version.rb +15 -0
  4. data/bin/tns_sid_enumeration.rb +52 -0
  5. data/bin/tns_sid_list.rb +43 -0
  6. data/lib/net/tns/client.rb +94 -0
  7. data/lib/net/tns/connection.rb +201 -0
  8. data/lib/net/tns/exceptions.rb +34 -0
  9. data/lib/net/tns/gem_version.rb +5 -0
  10. data/lib/net/tns/helpers/string_helpers.rb +49 -0
  11. data/lib/net/tns/packet.rb +82 -0
  12. data/lib/net/tns/packets/abort_packet.rb +12 -0
  13. data/lib/net/tns/packets/accept_packet.rb +19 -0
  14. data/lib/net/tns/packets/ack_packet.rb +7 -0
  15. data/lib/net/tns/packets/attention_packet.rb +9 -0
  16. data/lib/net/tns/packets/connect_packet.rb +55 -0
  17. data/lib/net/tns/packets/control_packet.rb +7 -0
  18. data/lib/net/tns/packets/data_packet.rb +20 -0
  19. data/lib/net/tns/packets/marker_packet.rb +18 -0
  20. data/lib/net/tns/packets/null_packet.rb +7 -0
  21. data/lib/net/tns/packets/redirect_packet.rb +10 -0
  22. data/lib/net/tns/packets/refuse_packet.rb +12 -0
  23. data/lib/net/tns/packets/resend_packet.rb +7 -0
  24. data/lib/net/tns/version.rb +15 -0
  25. data/lib/net/tns.rb +20 -0
  26. data/lib/net/tti/client.rb +90 -0
  27. data/lib/net/tti/connection.rb +142 -0
  28. data/lib/net/tti/crypto.rb +189 -0
  29. data/lib/net/tti/data_types/chunked_string.rb +63 -0
  30. data/lib/net/tti/data_types/key_value_pair.rb +25 -0
  31. data/lib/net/tti/data_types.rb +2 -0
  32. data/lib/net/tti/exceptions.rb +62 -0
  33. data/lib/net/tti/message.rb +50 -0
  34. data/lib/net/tti/messages/data_type_negotiation_request.rb +133 -0
  35. data/lib/net/tti/messages/data_type_negotiation_response.rb +9 -0
  36. data/lib/net/tti/messages/error_message.rb +14 -0
  37. data/lib/net/tti/messages/function_call.rb +46 -0
  38. data/lib/net/tti/messages/function_calls/authentication.rb +45 -0
  39. data/lib/net/tti/messages/function_calls/authentication_x64.rb +42 -0
  40. data/lib/net/tti/messages/function_calls/authentication_x86.rb +53 -0
  41. data/lib/net/tti/messages/function_calls/pre_authentication_response.rb +32 -0
  42. data/lib/net/tti/messages/protocol_negotiation_request.rb +29 -0
  43. data/lib/net/tti/messages/protocol_negotiation_response.rb +44 -0
  44. data/lib/net/tti.rb +18 -0
  45. 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,12 @@
1
+ module Net
2
+ module TNS
3
+ class AbortPacket < Packet
4
+ register_tns_type 9
5
+
6
+ uint8 :user_reason
7
+ uint8 :system_reason
8
+ # not sure if this is correct, it's just what wireshark seems to do
9
+ rest :data
10
+ end
11
+ end
12
+ end
@@ -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,7 @@
1
+ module Net
2
+ module TNS
3
+ class AckPacket < Packet
4
+ register_tns_type 3
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module Net
2
+ module TNS
3
+ class AttentionPacket < Packet
4
+ register_tns_type 13
5
+
6
+ rest :data
7
+ end
8
+ end
9
+ 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,7 @@
1
+ module Net
2
+ module TNS
3
+ class ControlPacket < Packet
4
+ register_tns_type 14
5
+ end
6
+ end
7
+ 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,7 @@
1
+ module Net
2
+ module TNS
3
+ class NullPacket < Packet
4
+ register_tns_type 7
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Net
2
+ module TNS
3
+ class RedirectPacket < Packet
4
+ register_tns_type 5
5
+
6
+ uint16be :data_length
7
+ string :data, :read_length => :data_length
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Net
2
+ module TNS
3
+ class RefusePacket < Packet
4
+ register_tns_type 4
5
+
6
+ uint8 :user_reason
7
+ uint8 :system_reason
8
+ uint16be :data_length
9
+ string :data, :read_length => :data_length
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Net
2
+ module TNS
3
+ class ResendPacket < Packet
4
+ register_tns_type 11
5
+ end
6
+ end
7
+ 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