-
Notifications
You must be signed in to change notification settings - Fork 97
Expand file tree
/
Copy pathdouble.lua
More file actions
301 lines (253 loc) · 8.48 KB
/
Copy pathdouble.lua
File metadata and controls
301 lines (253 loc) · 8.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
-- This code is ugly; but it is largely an attempt to deal with disparate
-- datastructures that are essentially isomorphic with a minimal amount
-- of looping.
-- wordpress testing hack
local new_session = true
package.cpath = package.cpath .. ";/usr/local/lib/lua/5.1/?.so"
package.path = package.path .. ";/usr/local/share/lua/5.1/?.lua"
local CRYPTDB_DIR = os.getenv("EDBDIR")
local get_locklib =
assert(package.loadlib(CRYPTDB_DIR .. "/obj/scripts/locklib.so",
"luaopen_locklib"))
get_locklib()
local luasql = assert(require("luasql.mysql"))
local proto = assert(require("mysql.proto"))
local lanes = assert(require("lanes"))
if require("lanes").configure then
lanes.configure()
end
local linda = lanes.linda()
local LOCK_FILE = "cdb.lock"
local LOG_FILE_PATH = CRYPTDB_DIR .. "/logs/double.log"
local cryptdb_lane = nil
local log_file_h = nil
-- we don't want crosstalk between different instances of 'double'
local RESULTS_QUEUE = "results_" .. math.random(10000)
local QUERY_QUEUE = "query_" .. math.random(10000)
function connect_server()
print("Double Connection.")
-- initialize pre-emptive thread
local cryptdb_lane_gen =
lanes.gen("*", {required = {'luasql.mysql',}}, exec_q)
cryptdb_lane = cryptdb_lane_gen()
-- open log file
log_file_h = assert(io.open(LOG_FILE_PATH, "a"))
end
function disconnect_client()
if log_file_h then
log_file_h:close()
end
end
function read_query(packet)
local query = string.sub(packet, 2)
-- don't acquire lock or do anything with cryptdb if the query is blank
-- > must be handled seperately because a blank query doesn't properly
-- trigger 'read_query_result(...)'
if 0 == string.len(query:gsub("%s+", "")) then
return nil
end
-- HACK: turns off strict mode for wordpress because
-- the testing database runs in strict mode.
-- > Don't send to CryptDB.
if true == new_session then
mode = "SET @@session.sql_mode := ''"
proxy.queries:append(1337, string.char(proxy.COM_QUERY) .. mode,
{resultset_is_needed = true})
new_session = false
end
local cryptdb_query
-- acquire lock and build queries
status, lock_fd = locklib.acquire_lock(LOCK_FILE)
if false == status then
print("Swallowed Query: [" .. query .. "]")
return nil
else
if string.byte(packet) == proxy.COM_INIT_DB then
cryptdb_query = "USE " .. query
else
cryptdb_query = query
end
proxy.queries:append(42, packet, {resultset_is_needed = true})
end
-- Clear the queues
linda:set(QUERY_QUEUE)
linda:set(RESULTS_QUEUE)
-- Send the new query
linda:send(QUERY_QUEUE, cryptdb_query)
return proxy.PROXY_SEND_QUERY
end
function read_query_result(inj)
local client_name = proxy.connection.client.src.name
local query = string.sub(inj.query, 2)
local out_status = nil
if nil == lock_fd then
if log_file_h then
log_file_h:write(create_log_entry(client_name, query, false,
false,
"lock acquisition failed"))
log_file_h:flush()
end
return
end
-- > somemtimes this is a table (ie SELECT), sometimes it's nil (query
-- error), sometimes it's number of rows affected by command
key, cryptdb_results = linda:receive(7.0, RESULTS_QUEUE)
local cryptdb_error = not cryptdb_results
local regular_error = proxy.MYSQLD_PACKET_ERR ==
inj.resultset.query_status
if regular_error or cryptdb_error then
out_status = "error"
elseif "string" == type(cryptdb_results) then
out_status = cryptdb_results
elseif "number" == type(cryptdb_results) then
-- WARN: this is always going to give a nonsensical result
-- for UPDATE and DDL queries.
out_status =
get_match_text(cryptdb_results == inj.resultset.affected_rows)
elseif nil == inj.resultset.rows then
out_status = "no result data from regular db"
else
-- do the naive comparison while gathering regular results,
-- then if it fails, do the slow comparison
local regular_results = {}
-- HACK/CARE: double iteration
local index = 1
local no_fast_match = false
for regular_row in inj.resultset.rows do
if false == no_fast_match then
-- don't do table_test if we already know we dont have
-- a fast match
if false == table_test(cryptdb_results[index], regular_row) then
no_fast_match = true
end
end
regular_results[index] = regular_row
index = index + 1
end
if #regular_results ~= #cryptdb_results then
out_status = get_match_text(false)
elseif false == no_fast_match then
out_status = get_match_text(true)
else
-- do slow, unordered matching
local test = slow_test(regular_results, cryptdb_results)
out_status = get_match_text(test)
end
end
if log_file_h then
log_file_h:write(create_log_entry(client_name, query,
cryptdb_error, regular_error,
out_status))
log_file_h:flush()
end
-- release lock
assert(lock_fd)
locklib.release_lock(lock_fd)
lock_fd = nil
end
-- never returns (sends messages)
function exec_q()
-- init
init = function ()
local luasql = require("luasql.mysql")
if nil == luasql then
return nil
end
local env = luasql.mysql()
if nil == env then
return nil
end
return env:connect("", "root", "letmein", "127.0.0.1", 3307)
end
local c = init()
-- failure loop
if nil == c then
io.stderr:write("\nERROR: failed to connect to cryptdb\n\n")
while true do
local _ = linda:receive(1.0, QUERY_QUEUE)
linda:send(RESULTS_QUEUE, nil)
end
end
-- query/parse loop
while true do
-- get query
local key, query = linda:receive(1.0, QUERY_QUEUE)
if nil ~= query then
-- do query
local cursor = c:execute(query)
-- gather results
local result = {}
if nil == cursor then
result = nil
elseif "number" == type(cursor) then
result = cursor
else
local row = cursor:fetch({}, "n")
local index = 1
while row do
result[index] = row
row = cursor:fetch({}, "n")
index = index + 1
end
end
linda:send(RESULTS_QUEUE, result)
end
end
end
function create_log_entry(client, query, cryptdb_error, regular_error,
status)
return client .. "," .. csv_escape(query) .. "," .. os.date("%c") .. "," .. ppbool(cryptdb_error) .. "," .. ppbool(regular_error) .. "," .. status .. "\n"
end
function table_test(results_a, results_b)
if type(results_a) == "nil" or type(results_b) == "nil" then
return false
end
if table.getn(results_a) ~= table.getn(results_b) then
return false
end
for i = 1, #results_a do
if results_a[i] ~= results_b[i] then
return false
end
end
return true
end
-- FIXME: Can get 2x speed up by removing matched elements from the inner
-- lookup array.
function slow_test(results_a, results_b)
if table.getn(results_a) ~= table.getn(results_b) then
return false
end
for a_index = 1, #results_a do
local matched = false
for b_index = 1, #results_b do
if table_test(results_a[a_index], results_b[b_index]) then
matched = true
break
end
end
if false == matched then
return false
end
end
return true
end
function get_match_text(b)
if true == b then
return "matched"
else
return "diverged"
end
end
function ppbool(b)
if true == b then
return "true"
else
return "false"
end
end
-- FIXME: Implement this if it actually matters; will make code slower.
-- VAPORWARE
function csv_escape(string)
return string
end