1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2012-2015 The Swoole Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.0 of the Apache license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http:// www.apache.org/licenses/LICENSE-2.0.html |
11 | If you did not receive a copy of the Apache2.0 license and are unable|
12 | to obtain it through the world-wide-web, please send a note to |
13 | license@swoole.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Twosee <twose@qq.com> |
16 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "php_swoole_mysql_proto.h"
21
22 using namespace swoole::mysql;
23
24 namespace swoole {
25 namespace mysql {
26 struct charset_t {
27 uint nr;
28 const char *name;
29 const char *collation;
30 };
31
get_charset(const char * name)32 char get_charset(const char *name) {
33 static const charset_t charsets[] = {
34 {1, "big5", "big5_chinese_ci"},
35 {3, "dec8", "dec8_swedish_ci"},
36 {4, "cp850", "cp850_general_ci"},
37 {6, "hp8", "hp8_english_ci"},
38 {7, "koi8r", "koi8r_general_ci"},
39 {8, "latin1", "latin1_swedish_ci"},
40 {5, "latin1", "latin1_german1_ci"},
41 {9, "latin2", "latin2_general_ci"},
42 {2, "latin2", "latin2_czech_cs"},
43 {10, "swe7", "swe7_swedish_ci"},
44 {11, "ascii", "ascii_general_ci"},
45 {12, "ujis", "ujis_japanese_ci"},
46 {13, "sjis", "sjis_japanese_ci"},
47 {16, "hebrew", "hebrew_general_ci"},
48 {17, "filename", "filename"},
49 {18, "tis620", "tis620_thai_ci"},
50 {19, "euckr", "euckr_korean_ci"},
51 {21, "latin2", "latin2_hungarian_ci"},
52 {27, "latin2", "latin2_croatian_ci"},
53 {22, "koi8u", "koi8u_general_ci"},
54 {24, "gb2312", "gb2312_chinese_ci"},
55 {25, "greek", "greek_general_ci"},
56 {26, "cp1250", "cp1250_general_ci"},
57 {28, "gbk", "gbk_chinese_ci"},
58 {30, "latin5", "latin5_turkish_ci"},
59 {31, "latin1", "latin1_german2_ci"},
60 {15, "latin1", "latin1_danish_ci"},
61 {32, "armscii8", "armscii8_general_ci"},
62 {33, "utf8", "utf8_general_ci"},
63 {35, "ucs2", "ucs2_general_ci"},
64 {36, "cp866", "cp866_general_ci"},
65 {37, "keybcs2", "keybcs2_general_ci"},
66 {38, "macce", "macce_general_ci"},
67 {39, "macroman", "macroman_general_ci"},
68 {40, "cp852", "cp852_general_ci"},
69 {41, "latin7", "latin7_general_ci"},
70 {20, "latin7", "latin7_estonian_cs"},
71 {57, "cp1256", "cp1256_general_ci"},
72 {59, "cp1257", "cp1257_general_ci"},
73 {63, "binary", "binary"},
74 {97, "eucjpms", "eucjpms_japanese_ci"},
75 {29, "cp1257", "cp1257_lithuanian_ci"},
76 {31, "latin1", "latin1_german2_ci"},
77 {34, "cp1250", "cp1250_czech_cs"},
78 {42, "latin7", "latin7_general_cs"},
79 {43, "macce", "macce_bin"},
80 {44, "cp1250", "cp1250_croatian_ci"},
81 {45, "utf8mb4", "utf8mb4_general_ci"},
82 {46, "utf8mb4", "utf8mb4_bin"},
83 {47, "latin1", "latin1_bin"},
84 {48, "latin1", "latin1_general_ci"},
85 {49, "latin1", "latin1_general_cs"},
86 {51, "cp1251", "cp1251_general_ci"},
87 {14, "cp1251", "cp1251_bulgarian_ci"},
88 {23, "cp1251", "cp1251_ukrainian_ci"},
89 {50, "cp1251", "cp1251_bin"},
90 {52, "cp1251", "cp1251_general_cs"},
91 {53, "macroman", "macroman_bin"},
92 {54, "utf16", "utf16_general_ci"},
93 {55, "utf16", "utf16_bin"},
94 {56, "utf16le", "utf16le_general_ci"},
95 {58, "cp1257", "cp1257_bin"},
96 {60, "utf32", "utf32_general_ci"},
97 {61, "utf32", "utf32_bin"},
98 {62, "utf16le", "utf16le_bin"},
99 {64, "armscii8", "armscii8_bin"},
100 {65, "ascii", "ascii_bin"},
101 {66, "cp1250", "cp1250_bin"},
102 {67, "cp1256", "cp1256_bin"},
103 {68, "cp866", "cp866_bin"},
104 {69, "dec8", "dec8_bin"},
105 {70, "greek", "greek_bin"},
106 {71, "hebrew", "hebrew_bin"},
107 {72, "hp8", "hp8_bin"},
108 {73, "keybcs2", "keybcs2_bin"},
109 {74, "koi8r", "koi8r_bin"},
110 {75, "koi8u", "koi8u_bin"},
111 {77, "latin2", "latin2_bin"},
112 {78, "latin5", "latin5_bin"},
113 {79, "latin7", "latin7_bin"},
114 {80, "cp850", "cp850_bin"},
115 {81, "cp852", "cp852_bin"},
116 {82, "swe7", "swe7_bin"},
117 {83, "utf8", "utf8_bin"},
118 {84, "big5", "big5_bin"},
119 {85, "euckr", "euckr_bin"},
120 {86, "gb2312", "gb2312_bin"},
121 {87, "gbk", "gbk_bin"},
122 {88, "sjis", "sjis_bin"},
123 {89, "tis620", "tis620_bin"},
124 {90, "ucs2", "ucs2_bin"},
125 {91, "ujis", "ujis_bin"},
126 {92, "geostd8", "geostd8_general_ci"},
127 {93, "geostd8", "geostd8_bin"},
128 {94, "latin1", "latin1_spanish_ci"},
129 {95, "cp932", "cp932_japanese_ci"},
130 {96, "cp932", "cp932_bin"},
131 {97, "eucjpms", "eucjpms_japanese_ci"},
132 {98, "eucjpms", "eucjpms_bin"},
133 {99, "cp1250", "cp1250_polish_ci"},
134 {128, "ucs2", "ucs2_unicode_ci"},
135 {129, "ucs2", "ucs2_icelandic_ci"},
136 {130, "ucs2", "ucs2_latvian_ci"},
137 {131, "ucs2", "ucs2_romanian_ci"},
138 {132, "ucs2", "ucs2_slovenian_ci"},
139 {133, "ucs2", "ucs2_polish_ci"},
140 {134, "ucs2", "ucs2_estonian_ci"},
141 {135, "ucs2", "ucs2_spanish_ci"},
142 {136, "ucs2", "ucs2_swedish_ci"},
143 {137, "ucs2", "ucs2_turkish_ci"},
144 {138, "ucs2", "ucs2_czech_ci"},
145 {139, "ucs2", "ucs2_danish_ci"},
146 {140, "ucs2", "ucs2_lithuanian_ci"},
147 {141, "ucs2", "ucs2_slovak_ci"},
148 {142, "ucs2", "ucs2_spanish2_ci"},
149 {143, "ucs2", "ucs2_roman_ci"},
150 {144, "ucs2", "ucs2_persian_ci"},
151 {145, "ucs2", "ucs2_esperanto_ci"},
152 {146, "ucs2", "ucs2_hungarian_ci"},
153 {147, "ucs2", "ucs2_sinhala_ci"},
154 {148, "ucs2", "ucs2_german2_ci"},
155 {149, "ucs2", "ucs2_croatian_ci"},
156 {150, "ucs2", "ucs2_unicode_520_ci"},
157 {151, "ucs2", "ucs2_vietnamese_ci"},
158 {160, "utf32", "utf32_unicode_ci"},
159 {161, "utf32", "utf32_icelandic_ci"},
160 {162, "utf32", "utf32_latvian_ci"},
161 {163, "utf32", "utf32_romanian_ci"},
162 {164, "utf32", "utf32_slovenian_ci"},
163 {165, "utf32", "utf32_polish_ci"},
164 {166, "utf32", "utf32_estonian_ci"},
165 {167, "utf32", "utf32_spanish_ci"},
166 {168, "utf32", "utf32_swedish_ci"},
167 {169, "utf32", "utf32_turkish_ci"},
168 {170, "utf32", "utf32_czech_ci"},
169 {171, "utf32", "utf32_danish_ci"},
170 {172, "utf32", "utf32_lithuanian_ci"},
171 {173, "utf32", "utf32_slovak_ci"},
172 {174, "utf32", "utf32_spanish2_ci"},
173 {175, "utf32", "utf32_roman_ci"},
174 {176, "utf32", "utf32_persian_ci"},
175 {177, "utf32", "utf32_esperanto_ci"},
176 {178, "utf32", "utf32_hungarian_ci"},
177 {179, "utf32", "utf32_sinhala_ci"},
178 {180, "utf32", "utf32_german2_ci"},
179 {181, "utf32", "utf32_croatian_ci"},
180 {182, "utf32", "utf32_unicode_520_ci"},
181 {183, "utf32", "utf32_vietnamese_ci"},
182 {192, "utf8", "utf8_unicode_ci"},
183 {193, "utf8", "utf8_icelandic_ci"},
184 {194, "utf8", "utf8_latvian_ci"},
185 {195, "utf8", "utf8_romanian_ci"},
186 {196, "utf8", "utf8_slovenian_ci"},
187 {197, "utf8", "utf8_polish_ci"},
188 {198, "utf8", "utf8_estonian_ci"},
189 {199, "utf8", "utf8_spanish_ci"},
190 {200, "utf8", "utf8_swedish_ci"},
191 {201, "utf8", "utf8_turkish_ci"},
192 {202, "utf8", "utf8_czech_ci"},
193 {203, "utf8", "utf8_danish_ci"},
194 {204, "utf8", "utf8_lithuanian_ci"},
195 {205, "utf8", "utf8_slovak_ci"},
196 {206, "utf8", "utf8_spanish2_ci"},
197 {207, "utf8", "utf8_roman_ci"},
198 {208, "utf8", "utf8_persian_ci"},
199 {209, "utf8", "utf8_esperanto_ci"},
200 {210, "utf8", "utf8_hungarian_ci"},
201 {211, "utf8", "utf8_sinhala_ci"},
202 {212, "utf8", "utf8_german2_ci"},
203 {213, "utf8", "utf8_croatian_ci"},
204 {214, "utf8", "utf8_unicode_520_ci"},
205 {215, "utf8", "utf8_vietnamese_ci"},
206 {224, "utf8mb4", "utf8mb4_unicode_ci"},
207 {225, "utf8mb4", "utf8mb4_icelandic_ci"},
208 {226, "utf8mb4", "utf8mb4_latvian_ci"},
209 {227, "utf8mb4", "utf8mb4_romanian_ci"},
210 {228, "utf8mb4", "utf8mb4_slovenian_ci"},
211 {229, "utf8mb4", "utf8mb4_polish_ci"},
212 {230, "utf8mb4", "utf8mb4_estonian_ci"},
213 {231, "utf8mb4", "utf8mb4_spanish_ci"},
214 {232, "utf8mb4", "utf8mb4_swedish_ci"},
215 {233, "utf8mb4", "utf8mb4_turkish_ci"},
216 {234, "utf8mb4", "utf8mb4_czech_ci"},
217 {235, "utf8mb4", "utf8mb4_danish_ci"},
218 {236, "utf8mb4", "utf8mb4_lithuanian_ci"},
219 {237, "utf8mb4", "utf8mb4_slovak_ci"},
220 {238, "utf8mb4", "utf8mb4_spanish2_ci"},
221 {239, "utf8mb4", "utf8mb4_roman_ci"},
222 {240, "utf8mb4", "utf8mb4_persian_ci"},
223 {241, "utf8mb4", "utf8mb4_esperanto_ci"},
224 {242, "utf8mb4", "utf8mb4_hungarian_ci"},
225 {243, "utf8mb4", "utf8mb4_sinhala_ci"},
226 {244, "utf8mb4", "utf8mb4_german2_ci"},
227 {245, "utf8mb4", "utf8mb4_croatian_ci"},
228 {246, "utf8mb4", "utf8mb4_unicode_520_ci"},
229 {247, "utf8mb4", "utf8mb4_vietnamese_ci"},
230 {248, "gb18030", "gb18030_chinese_ci"},
231 {249, "gb18030", "gb18030_bin"},
232 {254, "utf8", "utf8_general_cs"},
233 {0, nullptr, nullptr},
234 };
235 const charset_t *c = charsets;
236 while (c[0].nr) {
237 if (!strcasecmp(c->name, name)) {
238 return c->nr;
239 }
240 ++c;
241 }
242 return -1;
243 }
244
245 // clang-format off
get_static_type_size(uint8_t type)246 uint8_t get_static_type_size(uint8_t type)
247 {
248 static const uint8_t map[] =
249 {
250 0, // SW_MYSQL_TYPE_DECIMAL 0
251 sizeof(int8_t), // SW_MYSQL_TYPE_TINY 1
252 sizeof(int16_t), // SW_MYSQL_TYPE_SHORT 2
253 sizeof(int32_t), // SW_MYSQL_TYPE_LONG 3
254 sizeof(float), // SW_MYSQL_TYPE_FLOAT 4
255 sizeof(double), // SW_MYSQL_TYPE_DOUBLE 5
256 0, // SW_MYSQL_TYPE_NULL 6
257 0, // SW_MYSQL_TYPE_TIMESTAMP 7
258 sizeof(int64_t), // SW_MYSQL_TYPE_LONGLONG 8
259 sizeof(int32_t), // SW_MYSQL_TYPE_INT24 9
260 0, // SW_MYSQL_TYPE_DATE 10
261 0, // SW_MYSQL_TYPE_TIME 11
262 0, // SW_MYSQL_TYPE_DATETIME 12
263 sizeof(int16_t), // SW_MYSQL_TYPE_YEAR 13
264 0, 0, 0, 0, 0, 0,
265 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
266 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
267 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
268 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
269 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
270 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
271 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
272 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
273 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
274 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
275 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
276 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
277 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
279 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
280 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
281 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
282 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
283 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
284 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
285 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
286 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
287 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
288 0, 0, 0, 0, 0, 0
289 };
290 SW_ASSERT(sizeof(map) == UINT8_MAX + 1);
291 return map[type];
292 }
293 // clang-format on
294
sha1_password_with_nonce(char * buf,const char * nonce,const char * password)295 static uint32_t sha1_password_with_nonce(char *buf, const char *nonce, const char *password) {
296 char hash_0[20] = {};
297 php_swoole_sha1(password, strlen(password), (uchar *) hash_0);
298
299 char hash_1[20] = {};
300 php_swoole_sha1(hash_0, sizeof(hash_0), (uchar *) hash_1);
301
302 char str[40];
303 memcpy(str, nonce, 20);
304 memcpy(str + 20, hash_1, 20);
305
306 char hash_2[20];
307 php_swoole_sha1(str, sizeof(str), (uchar *) hash_2);
308
309 char hash_3[20];
310
311 int *a = (int *) hash_2;
312 int *b = (int *) hash_0;
313 int *c = (int *) hash_3;
314
315 int i;
316 for (i = 0; i < 5; i++) {
317 c[i] = a[i] ^ b[i];
318 }
319 memcpy(buf, hash_3, 20);
320 return 20;
321 }
322
sha256_password_with_nonce(char * buf,const char * nonce,const char * password)323 static uint32_t sha256_password_with_nonce(char *buf, const char *nonce, const char *password) {
324 // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce))
325 char hashed[32], double_hashed[32];
326 php_swoole_sha256(password, strlen(password), (unsigned char *) hashed);
327 php_swoole_sha256(hashed, 32, (unsigned char *) double_hashed);
328 char combined[32 + SW_MYSQL_NONCE_LENGTH]; // double-hashed + nonce
329 memcpy(combined, double_hashed, 32);
330 memcpy(combined + 32, nonce, SW_MYSQL_NONCE_LENGTH);
331 char xor_bytes[32];
332 php_swoole_sha256(combined, 32 + SW_MYSQL_NONCE_LENGTH, (unsigned char *) xor_bytes);
333 int i;
334 for (i = 0; i < 32; i++) {
335 hashed[i] ^= xor_bytes[i];
336 }
337 memcpy(buf, hashed, 32);
338 return 32;
339 }
340
341 /** @return: password length */
mysql_auth_encrypt_dispatch(char * buf,const std::string auth_plugin_name,const char * nonce,const char * password)342 static sw_inline uint32_t mysql_auth_encrypt_dispatch(char *buf,
343 const std::string auth_plugin_name,
344 const char *nonce,
345 const char *password) {
346 if (auth_plugin_name.length() == 0 || auth_plugin_name == "mysql_native_password") {
347 // mysql_native_password is default
348 return sha1_password_with_nonce(buf, nonce, password);
349 } else if (auth_plugin_name == "caching_sha2_password") {
350 return sha256_password_with_nonce(buf, nonce, password);
351 } else {
352 swoole_warning("Unknown auth plugin: %s", auth_plugin_name.c_str());
353 return 0;
354 }
355 }
356
eof_packet(const char * data)357 eof_packet::eof_packet(const char *data) : server_packet(data) {
358 swMysqlPacketDump(header.length, header.number, data, "EOF_Packet");
359 // EOF_Packet = Packet header (4 bytes) + 0xFE + warning(2byte) + status(2byte)
360 data += SW_MYSQL_PACKET_HEADER_SIZE;
361 // int<1> header [fe] EOF header
362 data += 1;
363 // int<2> warnings number of warnings
364 warning_count = sw_mysql_uint2korr2korr(data);
365 data += 2;
366 // int<2> status_flags Status Flags
367 server_status = sw_mysql_uint2korr2korr(data);
368 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "EOF_Packet, warnings=%u, status_code=%u", warning_count, server_status.status);
369 }
370
ok_packet(const char * data)371 ok_packet::ok_packet(const char *data) : server_packet(data) {
372 swMysqlPacketDump(header.length, header.number, data, "OK_Packet");
373 bool nul;
374 data += SW_MYSQL_PACKET_HEADER_SIZE;
375 // int<1> header [00] or [fe] the OK packet header
376 data += 1;
377 // int<lenenc> affected_rows affected rows
378 data += read_lcb(data, &affected_rows, &nul);
379 // int<lenenc> last_insert_id last insert id
380 data += read_lcb(data, &last_insert_id, &nul);
381 // int<2> status_flags status Flags
382 server_status = sw_mysql_uint2korr2korr(data);
383 data += 2;
384 // int<2> warnings number of warnings
385 warning_count = sw_mysql_uint2korr2korr(data);
386 // p += 2;
387 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
388 "OK_Packet, affected_rows=%" PRIu64 ", insert_id=%" PRIu64 ", status_flags=0x%08x, warnings=%u",
389 affected_rows,
390 last_insert_id,
391 server_status.status,
392 warning_count);
393 }
394
err_packet(const char * data)395 err_packet::err_packet(const char *data) : server_packet(data) {
396 swMysqlPacketDump(header.length, header.number, data, "ERR_Packet");
397 // ERR Packet = Packet header (4 bytes) + ERR Payload
398 data += SW_MYSQL_PACKET_HEADER_SIZE;
399 // int<1> header [ff] header of the ERR packet
400 data += 1;
401 // int<2> error_code error-code
402 code = sw_mysql_uint2korr2korr(data);
403 data += 2;
404 // string[1] sql_state_marker # marker of the SQL State
405 data += 1;
406 // string[5] sql_state SQL State
407 memcpy(sql_state, data, 5);
408 sql_state[5] = '\0';
409 data += 5;
410 // string<EOF> error_message human readable error message
411 msg = std::string(data, header.length - 9);
412 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
413 "ERR_Packet, error_code=%u, sql_state=%s, status_msg=[%s]",
414 code,
415 sql_state,
416 msg.c_str());
417 };
418
greeting_packet(const char * data)419 greeting_packet::greeting_packet(const char *data) : server_packet(data) {
420 swMysqlPacketDump(header.length, header.number, data, "Protocol::HandshakeGreeting");
421 /**
422 1 [0a] protocol version
423 string[NUL] server version
424 4 connection id
425 string[8] auth-plugin-data-part-1
426 1 [00] filler
427 2 capability flags (lower 2 bytes)
428 if more data in the packet:
429 1 character set
430 2 status flags
431 2 capability flags (upper 2 bytes)
432 if capabilities & CLIENT_PLUGIN_AUTH {
433 1 length of auth-plugin-data
434 } else {
435 1 [00]
436 }
437 string[10] reserved (all [00])
438 if capabilities & CLIENT_SECURE_CONNECTION {
439 string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))
440 if capabilities & CLIENT_PLUGIN_AUTH {
441 string[NUL] auth-plugin name
442 }
443 */
444 const char *p = data + SW_MYSQL_PACKET_HEADER_SIZE;
445 // 1 [0a] protocol version
446 protocol_version = *p;
447 p++;
448 // x server version
449 server_version = std::string(p);
450 p += server_version.length() + 1;
451 // 4 connection id
452 connection_id = *((int *) p);
453 p += 4;
454 // string[8] auth-plugin-data-part-1
455 memcpy(auth_plugin_data, p, 8);
456 p += 8;
457 // 1 [00] filler
458 filler = *p;
459 p += 1;
460 // 2 capability flags (lower 2 bytes)
461 memcpy(((char *) (&capability_flags)), p, 2);
462 p += 2;
463
464 if (p < data + header.length) {
465 // 1 character set
466 charset = *p;
467 p += 1;
468 // 2 status flags
469 memcpy(&status_flags, p, 2);
470 p += 2;
471 // 2 capability flags (upper 2 bytes)
472 memcpy(((char *) (&capability_flags) + 2), p, 2);
473 p += 2;
474 // 1 auth plugin data length
475 auth_plugin_data_length = (uint8_t) *p;
476 p += 1;
477 // x reserved
478 memcpy(&reserved, p, sizeof(reserved));
479 p += sizeof(reserved);
480 if (capability_flags & SW_MYSQL_CLIENT_SECURE_CONNECTION) {
481 uint8_t len = SW_MAX(13, auth_plugin_data_length - 8);
482 memcpy(auth_plugin_data + 8, p, len);
483 p += len;
484 }
485 if (capability_flags & SW_MYSQL_CLIENT_PLUGIN_AUTH) {
486 auth_plugin_name = std::string(p, strlen(p));
487 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "use %s auth plugin", auth_plugin_name.c_str());
488 }
489 }
490 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
491 "Server protocol=%d, version=%s, connection_id=%d, capabilites=0x%08x, status=%u, auth_plugin_name=%s, "
492 "auth_plugin_data=L%u[%s]",
493 protocol_version,
494 server_version.c_str(),
495 connection_id,
496 capability_flags,
497 status_flags.status,
498 auth_plugin_name.c_str(),
499 auth_plugin_data_length,
500 auth_plugin_data);
501 };
502
login_packet(greeting_packet * greeting_packet,const std::string & user,const std::string & password,std::string database,char charset)503 login_packet::login_packet(greeting_packet *greeting_packet,
504 const std::string &user,
505 const std::string &password,
506 std::string database,
507 char charset) {
508 char *p = data.body;
509 uint32_t tint;
510 // capability flags, CLIENT_PROTOCOL_41 always set
511 tint = SW_MYSQL_CLIENT_LONG_PASSWORD | SW_MYSQL_CLIENT_PROTOCOL_41 | SW_MYSQL_CLIENT_SECURE_CONNECTION |
512 SW_MYSQL_CLIENT_CONNECT_WITH_DB | SW_MYSQL_CLIENT_PLUGIN_AUTH | SW_MYSQL_CLIENT_MULTI_RESULTS;
513 memcpy(p, &tint, sizeof(tint));
514 p += sizeof(tint);
515 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client capabilites=0x%08x", tint);
516 // max-packet size
517 tint = 300;
518 memcpy(p, &tint, sizeof(tint));
519 p += sizeof(tint);
520 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client max packet=%u", tint);
521 // use the server character_set when the character_set is not set.
522 *p = charset ? charset : greeting_packet->charset;
523 p += 1;
524 // string[23] reserved (all [0])
525 p += 23;
526 // string[NUL] username
527 strcpy(p, user.c_str());
528 p += (user.length() + 1);
529 // string[NUL] password
530 if (password.length() > 0) {
531 *p = mysql_auth_encrypt_dispatch(
532 p + 1, greeting_packet->auth_plugin_name, greeting_packet->auth_plugin_data, password.c_str());
533 } else {
534 *p = 0;
535 }
536 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
537 "Client charset=%u, user=%s, password=%s, hased=L%d[%.*s], database=%s, auth_plugin_name=%s",
538 charset,
539 user.c_str(),
540 password.c_str(),
541 (int) *p,
542 (int) *p,
543 p + 1,
544 database.c_str(),
545 greeting_packet->auth_plugin_name.c_str());
546 p += (((uint32_t) *p) + 1);
547 // string[NUL] database
548 strcpy(p, database.c_str());
549 p += (database.length() + 1);
550 // string[NUL] auth plugin name
551 strcpy(p, greeting_packet->auth_plugin_name.c_str());
552 p += (greeting_packet->auth_plugin_name.length() + 1);
553 // packet header
554 set_header(p - data.body, greeting_packet->header.number + 1);
555 swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::HandshakeLogin");
556 }
557
auth_switch_request_packet(const char * data)558 auth_switch_request_packet::auth_switch_request_packet(const char *data) : server_packet(data) {
559 swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSwitchRequest");
560 // 4 header
561 data += SW_MYSQL_PACKET_HEADER_SIZE;
562 // 1 type
563 data += 1;
564 // string[NUL] auth_method_name
565 auth_method_name = std::string(data);
566 data += (auth_method_name.length() + 1);
567 // string[NUL] auth_method_data
568 strcpy(auth_method_data, data);
569 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "auth switch plugin name=%s", auth_method_name.c_str());
570 }
571
auth_switch_response_packet(auth_switch_request_packet * req,const std::string & password)572 auth_switch_response_packet::auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password) {
573 // if auth switch is triggered, password can't be empty
574 // create auth switch response packet
575 set_header(mysql_auth_encrypt_dispatch(data.body, req->auth_method_name, req->auth_method_data, password.c_str()),
576 req->header.number + 1);
577 swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse");
578 }
579
580 // Caching sha2 authentication. Public key request and send encrypted password
581 // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
auth_signature_response_packet(raw_data_packet * raw_data_pakcet,const std::string & password,const char * auth_plugin_data)582 auth_signature_response_packet::auth_signature_response_packet(raw_data_packet *raw_data_pakcet,
583 const std::string &password,
584 const char *auth_plugin_data) {
585 #ifndef SW_MYSQL_RSA_SUPPORT
586 {
587 swoole_warning(SW_MYSQL_NO_RSA_ERROR);
588 #else
589 if (0) {
590 _error:
591 #endif
592 data.body[0] = SW_MYSQL_AUTH_SIGNATURE_ERROR;
593 set_header(1, raw_data_pakcet->header.number + 1);
594 return;
595 }
596 #ifdef SW_MYSQL_RSA_SUPPORT
597 const char *tmp = raw_data_pakcet->body;
598 uint32_t rsa_public_key_length = raw_data_pakcet->header.length;
599 while (tmp[0] != 0x2d) {
600 tmp++; // ltrim
601 rsa_public_key_length--;
602 }
603 char rsa_public_key[rsa_public_key_length + 1]; // rsa + '\0'
604 memcpy((char *) rsa_public_key, tmp, rsa_public_key_length);
605 rsa_public_key[rsa_public_key_length] = '\0';
606 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
607 "rsa_public_key_length=%d;\nrsa_public_key=[%.*s]",
608 rsa_public_key_length,
609 rsa_public_key_length,
610 rsa_public_key);
611
612 size_t password_bytes_length = password.length() + 1;
613 unsigned char password_bytes[password_bytes_length];
614 // copy NUL terminator to password to stack
615 strcpy((char *) password_bytes, password.c_str());
616 // XOR the password bytes with the challenge
617 for (size_t i = 0; i < password_bytes_length; i++) // include '\0' byte
618 {
619 password_bytes[i] ^= auth_plugin_data[i % SW_MYSQL_NONCE_LENGTH];
620 }
621
622 // prepare RSA public key
623 BIO *bio = nullptr;
624 RSA *public_rsa = nullptr;
625 if (sw_unlikely((bio = BIO_new_mem_buf((void *) rsa_public_key, -1)) == nullptr)) {
626 swoole_warning("BIO_new_mem_buf publicKey error!");
627 goto _error;
628 }
629 // PEM_read_bio_RSA_PUBKEY
630 ERR_clear_error();
631 if (sw_unlikely((public_rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr)) == nullptr)) {
632 char err_buf[512];
633 ERR_load_crypto_strings();
634 ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
635 swoole_warning("[PEM_read_bio_RSA_PUBKEY ERROR]: %s", err_buf);
636 goto _error;
637 }
638 BIO_free_all(bio);
639 // encrypt with RSA public key
640 int rsa_len = RSA_size(public_rsa);
641 unsigned char encrypt_msg[rsa_len];
642 // RSA_public_encrypt
643 ERR_clear_error();
644 size_t flen = rsa_len - 42;
645 flen = password_bytes_length > flen ? flen : password_bytes_length;
646 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "rsa_len=%d", rsa_len);
647 if (sw_unlikely(RSA_public_encrypt(flen,
648 (const unsigned char *) password_bytes,
649 (unsigned char *) encrypt_msg,
650 public_rsa,
651 RSA_PKCS1_OAEP_PADDING) < 0)) {
652 char err_buf[512];
653 ERR_load_crypto_strings();
654 ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
655 swoole_warning("[RSA_public_encrypt ERROR]: %s", err_buf);
656 goto _error;
657 }
658 RSA_free(public_rsa);
659 memcpy(data.body, (char *) encrypt_msg, rsa_len); // copy rsa to buf
660 set_header(rsa_len, raw_data_pakcet->header.number + 1);
661 swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse");
662 #endif
663 }
664
665 void field_packet::parse(const char *data) {
666 server_packet::parse(data);
667 bool nul = false;
668 char *p = body = new char[header.length];
669 memcpy(body, data + SW_MYSQL_PACKET_HEADER_SIZE, header.length);
670 // catalog
671 p += read_lcb(p, &catalog_length, &nul);
672 catalog = p;
673 p += catalog_length;
674 // database
675 p += read_lcb(p, &database_length, &nul);
676 database = p;
677 p += database_length;
678 // table
679 p += read_lcb(p, &table_length, &nul);
680 table = p;
681 p += table_length;
682 // origin table
683 p += read_lcb(p, &org_table_length, &nul);
684 org_table = p;
685 p += org_table_length;
686 // name
687 p += read_lcb(p, &name_length, &nul);
688 name = p;
689 p += name_length;
690 // origin table
691 p += read_lcb(p, &org_name_length, &nul);
692 org_name = p;
693 p += org_name_length;
694 // filler
695 p += 1;
696 // charset
697 charset = sw_mysql_uint2korr2korr(p);
698 p += 2;
699 // binary length
700 length = sw_mysql_uint2korr4korr(p);
701 p += 4;
702 // field type
703 type = (uint8_t) *p;
704 p += 1;
705 // flags
706 flags = sw_mysql_uint2korr2korr(p);
707 p += 2;
708 /* decimals */
709 decimals = *p;
710 p += 1;
711 /* filler */
712 p += 2;
713 /* default - a priori facultatif */
714 if (p < body + header.length) {
715 p += read_lcb(p, &def_length, &nul);
716 def = p;
717 p += def_length;
718 }
719 swMysqlPacketDump(header.length, header.number, data, (*name == '?' ? "Protocol::Param" : "Protocol::Field"));
720 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
721 "catalog=%.*s, database=%.*s, table=%.*s, org_table=%.*s, name=%.*s, org_name=%.*s,"
722 "charset=%u, binary_length=%" PRIu64 ", type=%u, flags=0x%08x, decimals=%u, def=[%.*s]",
723 catalog_length,
724 catalog,
725 database_length,
726 database,
727 table_length,
728 table,
729 org_table_length,
730 org_table,
731 name_length,
732 name,
733 org_name_length,
734 org_name,
735 charset,
736 length,
737 type,
738 flags,
739 decimals,
740 def_length,
741 def);
742 }
743 } // namespace mysql
744 } // namespace swoole
745