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