net-tns 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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