sequel-impala 1.0.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG +16 -0
- data/LICENSE +2 -1
- data/README.md +45 -0
- data/lib/rbhive.rb +8 -0
- data/lib/rbhive/connection.rb +150 -0
- data/lib/rbhive/explain_result.rb +46 -0
- data/lib/rbhive/result_set.rb +37 -0
- data/lib/rbhive/schema_definition.rb +86 -0
- data/lib/rbhive/t_c_l_i_connection.rb +464 -0
- data/lib/rbhive/t_c_l_i_result_set.rb +3 -0
- data/lib/rbhive/t_c_l_i_schema_definition.rb +87 -0
- data/lib/rbhive/table_schema.rb +122 -0
- data/lib/rbhive/version.rb +3 -0
- data/lib/sequel/adapters/impala.rb +13 -1
- data/lib/sequel/adapters/rbhive.rb +174 -0
- data/lib/sequel/adapters/shared/impala.rb +11 -3
- data/lib/sequel/extensions/csv_to_parquet.rb +68 -14
- data/lib/thrift/facebook_service.rb +700 -0
- data/lib/thrift/fb303_constants.rb +9 -0
- data/lib/thrift/fb303_types.rb +19 -0
- data/lib/thrift/hive_metastore_constants.rb +41 -0
- data/lib/thrift/hive_metastore_types.rb +630 -0
- data/lib/thrift/hive_service_constants.rb +13 -0
- data/lib/thrift/hive_service_types.rb +72 -0
- data/lib/thrift/queryplan_constants.rb +13 -0
- data/lib/thrift/queryplan_types.rb +261 -0
- data/lib/thrift/sasl_client_transport.rb +161 -0
- data/lib/thrift/serde_constants.rb +92 -0
- data/lib/thrift/serde_types.rb +7 -0
- data/lib/thrift/t_c_l_i_service.rb +1054 -0
- data/lib/thrift/t_c_l_i_service_constants.rb +72 -0
- data/lib/thrift/t_c_l_i_service_types.rb +1768 -0
- data/lib/thrift/thrift_hive.rb +508 -0
- data/lib/thrift/thrift_hive_metastore.rb +3856 -0
- data/spec/impala_test.rb +6 -1
- metadata +53 -25
- data/README.rdoc +0 -39
@@ -0,0 +1,72 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift Compiler (0.9.0)
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'thrift'
|
8
|
+
require_relative 'fb303_types'
|
9
|
+
require_relative 'hive_metastore_types'
|
10
|
+
require_relative 'queryplan_types'
|
11
|
+
|
12
|
+
|
13
|
+
module Hive
|
14
|
+
module Thrift
|
15
|
+
module JobTrackerState
|
16
|
+
INITIALIZING = 1
|
17
|
+
RUNNING = 2
|
18
|
+
VALUE_MAP = {1 => "INITIALIZING", 2 => "RUNNING"}
|
19
|
+
VALID_VALUES = Set.new([INITIALIZING, RUNNING]).freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
class HiveClusterStatus
|
23
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
24
|
+
TASKTRACKERS = 1
|
25
|
+
MAPTASKS = 2
|
26
|
+
REDUCETASKS = 3
|
27
|
+
MAXMAPTASKS = 4
|
28
|
+
MAXREDUCETASKS = 5
|
29
|
+
STATE = 6
|
30
|
+
|
31
|
+
FIELDS = {
|
32
|
+
TASKTRACKERS => {:type => ::Thrift::Types::I32, :name => 'taskTrackers'},
|
33
|
+
MAPTASKS => {:type => ::Thrift::Types::I32, :name => 'mapTasks'},
|
34
|
+
REDUCETASKS => {:type => ::Thrift::Types::I32, :name => 'reduceTasks'},
|
35
|
+
MAXMAPTASKS => {:type => ::Thrift::Types::I32, :name => 'maxMapTasks'},
|
36
|
+
MAXREDUCETASKS => {:type => ::Thrift::Types::I32, :name => 'maxReduceTasks'},
|
37
|
+
STATE => {:type => ::Thrift::Types::I32, :name => 'state', :enum_class => ::Hive::Thrift::JobTrackerState}
|
38
|
+
}
|
39
|
+
|
40
|
+
def struct_fields; FIELDS; end
|
41
|
+
|
42
|
+
def validate
|
43
|
+
unless @state.nil? || ::Hive::Thrift::JobTrackerState::VALID_VALUES.include?(@state)
|
44
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field state!')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
::Thrift::Struct.generate_accessors self
|
49
|
+
end
|
50
|
+
|
51
|
+
class HiveServerException < ::Thrift::Exception
|
52
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
53
|
+
MESSAGE = 1
|
54
|
+
ERRORCODE = 2
|
55
|
+
SQLSTATE = 3
|
56
|
+
|
57
|
+
FIELDS = {
|
58
|
+
MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'},
|
59
|
+
ERRORCODE => {:type => ::Thrift::Types::I32, :name => 'errorCode'},
|
60
|
+
SQLSTATE => {:type => ::Thrift::Types::STRING, :name => 'SQLState'}
|
61
|
+
}
|
62
|
+
|
63
|
+
def struct_fields; FIELDS; end
|
64
|
+
|
65
|
+
def validate
|
66
|
+
end
|
67
|
+
|
68
|
+
::Thrift::Struct.generate_accessors self
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift Compiler (0.9.0)
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'thrift'
|
8
|
+
|
9
|
+
module Hive
|
10
|
+
module Thrift
|
11
|
+
module AdjacencyType
|
12
|
+
CONJUNCTIVE = 0
|
13
|
+
DISJUNCTIVE = 1
|
14
|
+
VALUE_MAP = {0 => "CONJUNCTIVE", 1 => "DISJUNCTIVE"}
|
15
|
+
VALID_VALUES = Set.new([CONJUNCTIVE, DISJUNCTIVE]).freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
module NodeType
|
19
|
+
OPERATOR = 0
|
20
|
+
STAGE = 1
|
21
|
+
VALUE_MAP = {0 => "OPERATOR", 1 => "STAGE"}
|
22
|
+
VALID_VALUES = Set.new([OPERATOR, STAGE]).freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
module OperatorType
|
26
|
+
JOIN = 0
|
27
|
+
MAPJOIN = 1
|
28
|
+
EXTRACT = 2
|
29
|
+
FILTER = 3
|
30
|
+
FORWARD = 4
|
31
|
+
GROUPBY = 5
|
32
|
+
LIMIT = 6
|
33
|
+
SCRIPT = 7
|
34
|
+
SELECT = 8
|
35
|
+
TABLESCAN = 9
|
36
|
+
FILESINK = 10
|
37
|
+
REDUCESINK = 11
|
38
|
+
UNION = 12
|
39
|
+
UDTF = 13
|
40
|
+
LATERALVIEWJOIN = 14
|
41
|
+
LATERALVIEWFORWARD = 15
|
42
|
+
HASHTABLESINK = 16
|
43
|
+
HASHTABLEDUMMY = 17
|
44
|
+
VALUE_MAP = {0 => "JOIN", 1 => "MAPJOIN", 2 => "EXTRACT", 3 => "FILTER", 4 => "FORWARD", 5 => "GROUPBY", 6 => "LIMIT", 7 => "SCRIPT", 8 => "SELECT", 9 => "TABLESCAN", 10 => "FILESINK", 11 => "REDUCESINK", 12 => "UNION", 13 => "UDTF", 14 => "LATERALVIEWJOIN", 15 => "LATERALVIEWFORWARD", 16 => "HASHTABLESINK", 17 => "HASHTABLEDUMMY"}
|
45
|
+
VALID_VALUES = Set.new([JOIN, MAPJOIN, EXTRACT, FILTER, FORWARD, GROUPBY, LIMIT, SCRIPT, SELECT, TABLESCAN, FILESINK, REDUCESINK, UNION, UDTF, LATERALVIEWJOIN, LATERALVIEWFORWARD, HASHTABLESINK, HASHTABLEDUMMY]).freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
module TaskType
|
49
|
+
MAP = 0
|
50
|
+
REDUCE = 1
|
51
|
+
OTHER = 2
|
52
|
+
VALUE_MAP = {0 => "MAP", 1 => "REDUCE", 2 => "OTHER"}
|
53
|
+
VALID_VALUES = Set.new([MAP, REDUCE, OTHER]).freeze
|
54
|
+
end
|
55
|
+
|
56
|
+
module StageType
|
57
|
+
CONDITIONAL = 0
|
58
|
+
COPY = 1
|
59
|
+
DDL = 2
|
60
|
+
MAPRED = 3
|
61
|
+
EXPLAIN = 4
|
62
|
+
FETCH = 5
|
63
|
+
FUNC = 6
|
64
|
+
MAPREDLOCAL = 7
|
65
|
+
MOVE = 8
|
66
|
+
STATS = 9
|
67
|
+
VALUE_MAP = {0 => "CONDITIONAL", 1 => "COPY", 2 => "DDL", 3 => "MAPRED", 4 => "EXPLAIN", 5 => "FETCH", 6 => "FUNC", 7 => "MAPREDLOCAL", 8 => "MOVE", 9 => "STATS"}
|
68
|
+
VALID_VALUES = Set.new([CONDITIONAL, COPY, DDL, MAPRED, EXPLAIN, FETCH, FUNC, MAPREDLOCAL, MOVE, STATS]).freeze
|
69
|
+
end
|
70
|
+
|
71
|
+
class Adjacency
|
72
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
73
|
+
NODE = 1
|
74
|
+
CHILDREN = 2
|
75
|
+
ADJACENCYTYPE = 3
|
76
|
+
|
77
|
+
FIELDS = {
|
78
|
+
NODE => {:type => ::Thrift::Types::STRING, :name => 'node'},
|
79
|
+
CHILDREN => {:type => ::Thrift::Types::LIST, :name => 'children', :element => {:type => ::Thrift::Types::STRING}},
|
80
|
+
ADJACENCYTYPE => {:type => ::Thrift::Types::I32, :name => 'adjacencyType', :enum_class => ::Hive::Thrift::AdjacencyType}
|
81
|
+
}
|
82
|
+
|
83
|
+
def struct_fields; FIELDS; end
|
84
|
+
|
85
|
+
def validate
|
86
|
+
unless @adjacencyType.nil? || ::Hive::Thrift::AdjacencyType::VALID_VALUES.include?(@adjacencyType)
|
87
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field adjacencyType!')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
::Thrift::Struct.generate_accessors self
|
92
|
+
end
|
93
|
+
|
94
|
+
class Graph
|
95
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
96
|
+
NODETYPE = 1
|
97
|
+
ROOTS = 2
|
98
|
+
ADJACENCYLIST = 3
|
99
|
+
|
100
|
+
FIELDS = {
|
101
|
+
NODETYPE => {:type => ::Thrift::Types::I32, :name => 'nodeType', :enum_class => ::Hive::Thrift::NodeType},
|
102
|
+
ROOTS => {:type => ::Thrift::Types::LIST, :name => 'roots', :element => {:type => ::Thrift::Types::STRING}},
|
103
|
+
ADJACENCYLIST => {:type => ::Thrift::Types::LIST, :name => 'adjacencyList', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Hive::Thrift::Adjacency}}
|
104
|
+
}
|
105
|
+
|
106
|
+
def struct_fields; FIELDS; end
|
107
|
+
|
108
|
+
def validate
|
109
|
+
unless @nodeType.nil? || ::Hive::Thrift::NodeType::VALID_VALUES.include?(@nodeType)
|
110
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field nodeType!')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
::Thrift::Struct.generate_accessors self
|
115
|
+
end
|
116
|
+
|
117
|
+
class Operator
|
118
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
119
|
+
OPERATORID = 1
|
120
|
+
OPERATORTYPE = 2
|
121
|
+
OPERATORATTRIBUTES = 3
|
122
|
+
OPERATORCOUNTERS = 4
|
123
|
+
DONE = 5
|
124
|
+
STARTED = 6
|
125
|
+
|
126
|
+
FIELDS = {
|
127
|
+
OPERATORID => {:type => ::Thrift::Types::STRING, :name => 'operatorId'},
|
128
|
+
OPERATORTYPE => {:type => ::Thrift::Types::I32, :name => 'operatorType', :enum_class => ::Hive::Thrift::OperatorType},
|
129
|
+
OPERATORATTRIBUTES => {:type => ::Thrift::Types::MAP, :name => 'operatorAttributes', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}},
|
130
|
+
OPERATORCOUNTERS => {:type => ::Thrift::Types::MAP, :name => 'operatorCounters', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::I64}},
|
131
|
+
DONE => {:type => ::Thrift::Types::BOOL, :name => 'done'},
|
132
|
+
STARTED => {:type => ::Thrift::Types::BOOL, :name => 'started'}
|
133
|
+
}
|
134
|
+
|
135
|
+
def struct_fields; FIELDS; end
|
136
|
+
|
137
|
+
def validate
|
138
|
+
unless @operatorType.nil? || ::Hive::Thrift::OperatorType::VALID_VALUES.include?(@operatorType)
|
139
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field operatorType!')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
::Thrift::Struct.generate_accessors self
|
144
|
+
end
|
145
|
+
|
146
|
+
class Task
|
147
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
148
|
+
TASKID = 1
|
149
|
+
TASKTYPE = 2
|
150
|
+
TASKATTRIBUTES = 3
|
151
|
+
TASKCOUNTERS = 4
|
152
|
+
OPERATORGRAPH = 5
|
153
|
+
OPERATORLIST = 6
|
154
|
+
DONE = 7
|
155
|
+
STARTED = 8
|
156
|
+
|
157
|
+
FIELDS = {
|
158
|
+
TASKID => {:type => ::Thrift::Types::STRING, :name => 'taskId'},
|
159
|
+
TASKTYPE => {:type => ::Thrift::Types::I32, :name => 'taskType', :enum_class => ::Hive::Thrift::TaskType},
|
160
|
+
TASKATTRIBUTES => {:type => ::Thrift::Types::MAP, :name => 'taskAttributes', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}},
|
161
|
+
TASKCOUNTERS => {:type => ::Thrift::Types::MAP, :name => 'taskCounters', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::I64}},
|
162
|
+
OPERATORGRAPH => {:type => ::Thrift::Types::STRUCT, :name => 'operatorGraph', :class => ::Hive::Thrift::Graph, :optional => true},
|
163
|
+
OPERATORLIST => {:type => ::Thrift::Types::LIST, :name => 'operatorList', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Hive::Thrift::Operator}, :optional => true},
|
164
|
+
DONE => {:type => ::Thrift::Types::BOOL, :name => 'done'},
|
165
|
+
STARTED => {:type => ::Thrift::Types::BOOL, :name => 'started'}
|
166
|
+
}
|
167
|
+
|
168
|
+
def struct_fields; FIELDS; end
|
169
|
+
|
170
|
+
def validate
|
171
|
+
unless @taskType.nil? || ::Hive::Thrift::TaskType::VALID_VALUES.include?(@taskType)
|
172
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field taskType!')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
::Thrift::Struct.generate_accessors self
|
177
|
+
end
|
178
|
+
|
179
|
+
class Stage
|
180
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
181
|
+
STAGEID = 1
|
182
|
+
STAGETYPE = 2
|
183
|
+
STAGEATTRIBUTES = 3
|
184
|
+
STAGECOUNTERS = 4
|
185
|
+
TASKLIST = 5
|
186
|
+
DONE = 6
|
187
|
+
STARTED = 7
|
188
|
+
|
189
|
+
FIELDS = {
|
190
|
+
STAGEID => {:type => ::Thrift::Types::STRING, :name => 'stageId'},
|
191
|
+
STAGETYPE => {:type => ::Thrift::Types::I32, :name => 'stageType', :enum_class => ::Hive::Thrift::StageType},
|
192
|
+
STAGEATTRIBUTES => {:type => ::Thrift::Types::MAP, :name => 'stageAttributes', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}},
|
193
|
+
STAGECOUNTERS => {:type => ::Thrift::Types::MAP, :name => 'stageCounters', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::I64}},
|
194
|
+
TASKLIST => {:type => ::Thrift::Types::LIST, :name => 'taskList', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Hive::Thrift::Task}},
|
195
|
+
DONE => {:type => ::Thrift::Types::BOOL, :name => 'done'},
|
196
|
+
STARTED => {:type => ::Thrift::Types::BOOL, :name => 'started'}
|
197
|
+
}
|
198
|
+
|
199
|
+
def struct_fields; FIELDS; end
|
200
|
+
|
201
|
+
def validate
|
202
|
+
unless @stageType.nil? || ::Hive::Thrift::StageType::VALID_VALUES.include?(@stageType)
|
203
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field stageType!')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
::Thrift::Struct.generate_accessors self
|
208
|
+
end
|
209
|
+
|
210
|
+
class Query
|
211
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
212
|
+
QUERYID = 1
|
213
|
+
QUERYTYPE = 2
|
214
|
+
QUERYATTRIBUTES = 3
|
215
|
+
QUERYCOUNTERS = 4
|
216
|
+
STAGEGRAPH = 5
|
217
|
+
STAGELIST = 6
|
218
|
+
DONE = 7
|
219
|
+
STARTED = 8
|
220
|
+
|
221
|
+
FIELDS = {
|
222
|
+
QUERYID => {:type => ::Thrift::Types::STRING, :name => 'queryId'},
|
223
|
+
QUERYTYPE => {:type => ::Thrift::Types::STRING, :name => 'queryType'},
|
224
|
+
QUERYATTRIBUTES => {:type => ::Thrift::Types::MAP, :name => 'queryAttributes', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}},
|
225
|
+
QUERYCOUNTERS => {:type => ::Thrift::Types::MAP, :name => 'queryCounters', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::I64}},
|
226
|
+
STAGEGRAPH => {:type => ::Thrift::Types::STRUCT, :name => 'stageGraph', :class => ::Hive::Thrift::Graph},
|
227
|
+
STAGELIST => {:type => ::Thrift::Types::LIST, :name => 'stageList', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Hive::Thrift::Stage}},
|
228
|
+
DONE => {:type => ::Thrift::Types::BOOL, :name => 'done'},
|
229
|
+
STARTED => {:type => ::Thrift::Types::BOOL, :name => 'started'}
|
230
|
+
}
|
231
|
+
|
232
|
+
def struct_fields; FIELDS; end
|
233
|
+
|
234
|
+
def validate
|
235
|
+
end
|
236
|
+
|
237
|
+
::Thrift::Struct.generate_accessors self
|
238
|
+
end
|
239
|
+
|
240
|
+
class QueryPlan
|
241
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
242
|
+
QUERIES = 1
|
243
|
+
DONE = 2
|
244
|
+
STARTED = 3
|
245
|
+
|
246
|
+
FIELDS = {
|
247
|
+
QUERIES => {:type => ::Thrift::Types::LIST, :name => 'queries', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Hive::Thrift::Query}},
|
248
|
+
DONE => {:type => ::Thrift::Types::BOOL, :name => 'done'},
|
249
|
+
STARTED => {:type => ::Thrift::Types::BOOL, :name => 'started'}
|
250
|
+
}
|
251
|
+
|
252
|
+
def struct_fields; FIELDS; end
|
253
|
+
|
254
|
+
def validate
|
255
|
+
end
|
256
|
+
|
257
|
+
::Thrift::Struct.generate_accessors self
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Thrift
|
2
|
+
class SaslClientTransport < BufferedTransport
|
3
|
+
attr_reader :challenge, :sasl_complete
|
4
|
+
|
5
|
+
STATUS_BYTES = 1
|
6
|
+
PAYLOAD_LENGTH_BYTES = 4
|
7
|
+
AUTH_MECHANISM = 'PLAIN'
|
8
|
+
NEGOTIATION_STATUS = {
|
9
|
+
START: 0x01,
|
10
|
+
OK: 0x02,
|
11
|
+
BAD: 0x03,
|
12
|
+
ERROR: 0x04,
|
13
|
+
COMPLETE: 0x05
|
14
|
+
}
|
15
|
+
|
16
|
+
def initialize(transport, sasl_params={})
|
17
|
+
super(transport)
|
18
|
+
@challenge = nil
|
19
|
+
@sasl_username = sasl_params.fetch(:username, 'anonymous')
|
20
|
+
@sasl_password = sasl_params.fetch(:password, 'anonymous')
|
21
|
+
@sasl_mechanism = sasl_params.fetch(:mechanism, 'PLAIN')
|
22
|
+
raise 'Unknown SASL mechanism: #{@sasl_mechanism}' unless ['PLAIN', 'GSSAPI'].include? @sasl_mechanism
|
23
|
+
if @sasl_mechanism == 'GSSAPI'
|
24
|
+
require 'gssapi'
|
25
|
+
@sasl_remote_principal = sasl_params[:remote_principal]
|
26
|
+
@sasl_remote_host = sasl_params[:remote_host]
|
27
|
+
@gsscli = GSSAPI::Simple.new(@sasl_remote_host, @sasl_remote_principal)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def read(sz)
|
32
|
+
len, = @transport.read(PAYLOAD_LENGTH_BYTES).unpack('l>') if @rbuf.nil?
|
33
|
+
sz = len if len && sz > len
|
34
|
+
@index += sz
|
35
|
+
ret = @rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer
|
36
|
+
if ret.length < sz
|
37
|
+
sz -= ret.length
|
38
|
+
read_into_buffer(@rbuf, [sz, len || 0].max)
|
39
|
+
@index = sz
|
40
|
+
ret += @rbuf.slice(0, sz) || Bytes.empty_byte_buffer
|
41
|
+
end
|
42
|
+
ret
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_byte
|
46
|
+
reset_buffer! if @index >= @rbuf.size
|
47
|
+
@index += 1
|
48
|
+
Bytes.get_string_byte(@rbuf, @index - 1)
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_into_buffer(buffer, size)
|
52
|
+
i = 0
|
53
|
+
while i < size
|
54
|
+
reset_buffer! if @index >= @rbuf.size
|
55
|
+
byte = Bytes.get_string_byte(@rbuf, @index)
|
56
|
+
Bytes.set_string_byte(buffer, i, byte)
|
57
|
+
@index += 1
|
58
|
+
i += 1
|
59
|
+
end
|
60
|
+
i
|
61
|
+
end
|
62
|
+
|
63
|
+
def write(buf)
|
64
|
+
initiate_hand_shake unless sasl_complete
|
65
|
+
header = [buf.length].pack('l>')
|
66
|
+
@wbuf << (header + Bytes.force_binary_encoding(buf))
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def initiate_hand_shake
|
72
|
+
if @sasl_mechanism == 'GSSAPI'
|
73
|
+
initiate_hand_shake_gssapi
|
74
|
+
else
|
75
|
+
initiate_hand_shake_plain
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initiate_hand_shake_gssapi
|
80
|
+
token = @gsscli.init_context
|
81
|
+
header = [NEGOTIATION_STATUS[:START], @sasl_mechanism.length].pack('cl>')
|
82
|
+
@transport.write header + @sasl_mechanism
|
83
|
+
header = [NEGOTIATION_STATUS[:OK], token.length].pack('cl>')
|
84
|
+
@transport.write header + token
|
85
|
+
status, len = @transport.read(STATUS_BYTES + PAYLOAD_LENGTH_BYTES).unpack('cl>')
|
86
|
+
case status
|
87
|
+
when NEGOTIATION_STATUS[:BAD], NEGOTIATION_STATUS[:ERROR]
|
88
|
+
raise @transport.to_io.read(len)
|
89
|
+
when NEGOTIATION_STATUS[:COMPLETE]
|
90
|
+
raise "Not expecting COMPLETE at initial stage"
|
91
|
+
when NEGOTIATION_STATUS[:OK]
|
92
|
+
challenge = @transport.to_io.read len
|
93
|
+
unless @gsscli.init_context(challenge)
|
94
|
+
raise "GSSAPI: challenge provided by server could not be verified"
|
95
|
+
end
|
96
|
+
header = [NEGOTIATION_STATUS[:OK], 0].pack('cl>')
|
97
|
+
@transport.write header
|
98
|
+
status2, len = @transport.read(STATUS_BYTES + PAYLOAD_LENGTH_BYTES).unpack('cl>')
|
99
|
+
case status2
|
100
|
+
when NEGOTIATION_STATUS[:BAD], NEGOTIATION_STATUS[:ERROR]
|
101
|
+
raise @transport.to_io.read(len)
|
102
|
+
when NEGOTIATION_STATUS[:COMPLETE]
|
103
|
+
raise "Not expecting COMPLETE at second stage"
|
104
|
+
when NEGOTIATION_STATUS[:OK]
|
105
|
+
challenge = @transport.to_io.read len
|
106
|
+
unwrapped = @gsscli.unwrap_message(challenge)
|
107
|
+
rewrapped = @gsscli.wrap_message(unwrapped)
|
108
|
+
header = [NEGOTIATION_STATUS[:COMPLETE], rewrapped.length].pack('cl>')
|
109
|
+
@transport.write header + rewrapped
|
110
|
+
status3, len = @transport.read(STATUS_BYTES + PAYLOAD_LENGTH_BYTES).unpack('cl>')
|
111
|
+
case status3
|
112
|
+
when NEGOTIATION_STATUS[:BAD], NEGOTIATION_STATUS[:ERROR]
|
113
|
+
raise @transport.to_io.read(len)
|
114
|
+
when NEGOTIATION_STATUS[:COMPLETE]
|
115
|
+
s = @transport.to_io.read len
|
116
|
+
@sasl_complete = true
|
117
|
+
when NEGOTIATION_STATUS[:OK]
|
118
|
+
raise "Failed to complete GSS challenge exchange"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
@sasl_complete = true
|
123
|
+
end
|
124
|
+
|
125
|
+
def initiate_hand_shake_plain
|
126
|
+
header = [NEGOTIATION_STATUS[:START], AUTH_MECHANISM.length].pack('cl>')
|
127
|
+
@transport.write header + AUTH_MECHANISM
|
128
|
+
message = "[#{AUTH_MECHANISM}]\u0000#{@sasl_username}\u0000#{@sasl_password}"
|
129
|
+
header = [NEGOTIATION_STATUS[:OK], message.length].pack('cl>')
|
130
|
+
@transport.write header + message
|
131
|
+
status, len = @transport.read(STATUS_BYTES + PAYLOAD_LENGTH_BYTES).unpack('cl>')
|
132
|
+
case status
|
133
|
+
when NEGOTIATION_STATUS[:BAD], NEGOTIATION_STATUS[:ERROR]
|
134
|
+
raise @transport.to_io.read(len)
|
135
|
+
when NEGOTIATION_STATUS[:COMPLETE]
|
136
|
+
@challenge = @transport.to_io.read len
|
137
|
+
when NEGOTIATION_STATUS[:OK]
|
138
|
+
raise "Failed to complete challenge exchange: only NONE supported currently"
|
139
|
+
end
|
140
|
+
@sasl_complete = true
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def reset_buffer!
|
146
|
+
len, = @transport.read(PAYLOAD_LENGTH_BYTES).unpack('l>')
|
147
|
+
@rbuf = @transport.read(len)
|
148
|
+
while @rbuf.size < len
|
149
|
+
@rbuf << @transport.read(len - @rbuf.size)
|
150
|
+
end
|
151
|
+
@index = 0
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class SaslClientTransportFactory < BaseTransportFactory
|
156
|
+
def get_transport(transport)
|
157
|
+
return SaslClientTransport.new(transport)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|