1 /*
2   +----------------------------------------------------------------------+
3   | Swoole                                                               |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 2.0 of the Apache license,    |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | http://www.apache.org/licenses/LICENSE-2.0.html                      |
9   | If you did not receive a copy of the Apache2.0 license and are unable|
10   | to obtain it through the world-wide-web, please send a note to       |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Author: Twosee  <twose@qq.com>                                       |
14   | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
15   +----------------------------------------------------------------------+
16 */
17 
18 #pragma once
19 
20 #include "php_swoole_cxx.h"
21 #include "swoole_util.h"
22 
23 #ifdef SW_USE_OPENSSL
24 #ifndef OPENSSL_NO_RSA
25 #define SW_MYSQL_RSA_SUPPORT
26 #include <openssl/err.h>
27 #include <openssl/rsa.h>
28 #include <openssl/pem.h>
29 #endif
30 #endif
31 
32 enum sw_mysql_command
33 {
34     SW_MYSQL_COM_NULL = -1,
35     SW_MYSQL_COM_SLEEP = 0,
36     SW_MYSQL_COM_QUIT,
37     SW_MYSQL_COM_INIT_DB,
38     SW_MYSQL_COM_QUERY = 3,
39     SW_MYSQL_COM_FIELD_LIST,
40     SW_MYSQL_COM_CREATE_DB,
41     SW_MYSQL_COM_DROP_DB,
42     SW_MYSQL_COM_REFRESH,
43     SW_MYSQL_COM_SHUTDOWN,
44     SW_MYSQL_COM_STATISTICS,
45     SW_MYSQL_COM_PROCESS_INFO,
46     SW_MYSQL_COM_CONNECT,
47     SW_MYSQL_COM_PROCESS_KILL,
48     SW_MYSQL_COM_DEBUG,
49     SW_MYSQL_COM_PING,
50     SW_MYSQL_COM_TIME,
51     SW_MYSQL_COM_DELAYED_INSERT,
52     SW_MYSQL_COM_CHANGE_USER,
53     SW_MYSQL_COM_BINLOG_DUMP,
54     SW_MYSQL_COM_TABLE_DUMP,
55     SW_MYSQL_COM_CONNECT_OUT,
56     SW_MYSQL_COM_REGISTER_SLAVE,
57     SW_MYSQL_COM_STMT_PREPARE,
58     SW_MYSQL_COM_STMT_EXECUTE,
59     SW_MYSQL_COM_STMT_SEND_LONG_DATA,
60     SW_MYSQL_COM_STMT_CLOSE,
61     SW_MYSQL_COM_STMT_RESET,
62     SW_MYSQL_COM_SET_OPTION,
63     SW_MYSQL_COM_STMT_FETCH,
64     SW_MYSQL_COM_DAEMON,
65     SW_MYSQL_COM_END
66 };
67 
68 enum sw_mysql_handshake_state
69 {
70     SW_MYSQL_HANDSHAKE_WAIT_REQUEST,
71     SW_MYSQL_HANDSHAKE_WAIT_SWITCH,
72     SW_MYSQL_HANDSHAKE_WAIT_SIGNATURE,
73     SW_MYSQL_HANDSHAKE_WAIT_RSA,
74     SW_MYSQL_HANDSHAKE_WAIT_RESULT,
75     SW_MYSQL_HANDSHAKE_COMPLETED,
76 };
77 
78 #define SW_MYSQL_AUTH_SIGNATRUE_PACKET_LENGTH 2
79 
80 enum sw_mysql_auth_signature
81 {
82     SW_MYSQL_AUTH_SIGNATURE_ERROR              = 0x00, // get signature failed
83     SW_MYSQL_AUTH_SIGNATURE                    = 0x01,
84     SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED       = 0x02,
85     SW_MYSQL_AUTH_SIGNATURE_SUCCESS            = 0x03,
86     SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED = 0x04, // rsa required
87 };
88 
89 enum sw_mysql_command_flag
90 {
91     SW_MYSQL_COMMAND_FLAG_QUERY   = 1 << 4,
92     SW_MYSQL_COMMAND_FLAG_EXECUTE = 1 << 5,
93 };
94 
95 enum sw_mysql_state
96 {
97     SW_MYSQL_STATE_CLOSED               = 0,
98     SW_MYSQL_STATE_IDLE                 = 1,
99     SW_MYSQL_STATE_QUERY                = 2 | SW_MYSQL_COMMAND_FLAG_QUERY,
100     SW_MYSQL_STATE_QUERY_FETCH          = 3 | SW_MYSQL_COMMAND_FLAG_QUERY,
101     SW_MYSQL_STATE_QUERY_MORE_RESULTS   = 4 | SW_MYSQL_COMMAND_FLAG_QUERY,
102     SW_MYSQL_STATE_PREPARE              = 5 | SW_MYSQL_COMMAND_FLAG_QUERY,
103     SW_MYSQL_STATE_EXECUTE              = 6 | SW_MYSQL_COMMAND_FLAG_EXECUTE,
104     SW_MYSQL_STATE_EXECUTE_FETCH        = 7 | SW_MYSQL_COMMAND_FLAG_EXECUTE,
105     SW_MYSQL_STATE_EXECUTE_MORE_RESULTS = 8 | SW_MYSQL_COMMAND_FLAG_EXECUTE,
106 };
107 
108 enum sw_mysql_packet_types
109 {
110     SW_MYSQL_PACKET_OK   = 0x0,
111     SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST = 0x01,
112 
113     /* not defined in protocol */
114     SW_MYSQL_PACKET_RAW_DATA,
115     SW_MYSQL_PACKET_GREETING,
116     SW_MYSQL_PACKET_LOGIN,
117     SW_MYSQL_PACKET_AUTH_SWITCH_RESPONSE,
118     SW_MYSQL_PACKET_AUTH_SIGNATURE_RESPONSE,
119     SW_MYSQL_PACKET_LCB, // length coded binary
120     SW_MYSQL_PACKET_FIELD,
121     SW_MYSQL_PACKET_ROW_DATA,
122     SW_MYSQL_PACKET_PREPARE_STATEMENT,
123     /* ======================= */
124 
125     SW_MYSQL_PACKET_NULL = 0xfb,
126     SW_MYSQL_PACKET_EOF  = 0xfe,
127     SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST = 0xfe,
128     SW_MYSQL_PACKET_ERR  = 0xff
129 };
130 
131 enum sw_mysql_field_types
132 {
133     SW_MYSQL_TYPE_DECIMAL,
134     SW_MYSQL_TYPE_TINY,
135     SW_MYSQL_TYPE_SHORT,
136     SW_MYSQL_TYPE_LONG,
137     SW_MYSQL_TYPE_FLOAT,
138     SW_MYSQL_TYPE_DOUBLE,
139     SW_MYSQL_TYPE_NULL,
140     SW_MYSQL_TYPE_TIMESTAMP,
141     SW_MYSQL_TYPE_LONGLONG,
142     SW_MYSQL_TYPE_INT24,
143     SW_MYSQL_TYPE_DATE,
144     SW_MYSQL_TYPE_TIME,
145     SW_MYSQL_TYPE_DATETIME,
146     SW_MYSQL_TYPE_YEAR,
147     SW_MYSQL_TYPE_NEWDATE,
148     SW_MYSQL_TYPE_VARCHAR,
149     SW_MYSQL_TYPE_BIT,
150     SW_MYSQL_TYPE_JSON = 245,
151     SW_MYSQL_TYPE_NEWDECIMAL,
152     SW_MYSQL_TYPE_ENUM,
153     SW_MYSQL_TYPE_SET,
154     SW_MYSQL_TYPE_TINY_BLOB,
155     SW_MYSQL_TYPE_MEDIUM_BLOB,
156     SW_MYSQL_TYPE_LONG_BLOB,
157     SW_MYSQL_TYPE_BLOB,
158     SW_MYSQL_TYPE_VAR_STRING,
159     SW_MYSQL_TYPE_STRING,
160     SW_MYSQL_TYPE_GEOMETRY
161 };
162 
163 // ref: https://dev.mysql.com/doc/dev/mysql-server/8.0.0/group__group__cs__capabilities__flags.html
164 // use regex: "\#define[ ]+(CLIENT_[A-Z_\d]+)[ ]+(\(?[\dA-Z <]+\)?)\n[ ]+?[ ]+([\s\S ]+?\.) More\.\.\.\n?"
165 // to "SW_MYSQL_$1 = $2, /* $3 */"
166 enum sw_mysql_client_capability_flags
167 {
168     SW_MYSQL_CLIENT_LONG_PASSWORD = 1, /* Use the improved version of Old Password Authentication. */
169     SW_MYSQL_CLIENT_FOUND_ROWS = 2, /* Send found rows instead of affected rows in EOF_Packet. */
170     SW_MYSQL_CLIENT_LONG_FLAG = 4, /* Get all column flags. */
171     SW_MYSQL_CLIENT_CONNECT_WITH_DB = 8, /* Database (schema) name can be specified on connect in Handshake Response Packet. */
172     SW_MYSQL_CLIENT_NO_SCHEMA = 16, /* Don't allow database.table.column. */
173     SW_MYSQL_CLIENT_COMPRESS = 32, /* Compression protocol supported. */
174     SW_MYSQL_CLIENT_ODBC = 64, /* Special handling of ODBC behavior. */
175     SW_MYSQL_CLIENT_LOCAL_FILES = 128, /* Can use LOAD DATA LOCAL. */
176     SW_MYSQL_CLIENT_IGNORE_SPACE = 256, /* Ignore spaces before '('. */
177     SW_MYSQL_CLIENT_PROTOCOL_41 = 512, /* New 4.1 protocol. */
178     SW_MYSQL_CLIENT_INTERACTIVE = 1024, /* This is an interactive client. */
179     SW_MYSQL_CLIENT_SSL = 2048, /* Use SSL encryption for the session. */
180     SW_MYSQL_CLIENT_IGNORE_SIGPIPE = 4096, /* Client only flag. */
181     SW_MYSQL_CLIENT_TRANSACTIONS = 8192, /* Client knows about transactions. */
182     SW_MYSQL_CLIENT_RESERVED = 16384, /* flag for 4.1 protocol. */
183     SW_MYSQL_CLIENT_SECURE_CONNECTION = 32768, /* swoole custom name for RESERVED2.  */
184     SW_MYSQL_CLIENT_RESERVED2 = 32768, /* flag for 4.1 authentication. */
185     SW_MYSQL_CLIENT_MULTI_STATEMENTS = (1UL << 16), /* Enable/disable multi-stmt support. */
186     SW_MYSQL_CLIENT_MULTI_RESULTS = (1UL << 17), /* Enable/disable multi-results. */
187     SW_MYSQL_CLIENT_PS_MULTI_RESULTS = (1UL << 18), /* Multi-results and OUT parameters in PS-protocol. */
188     SW_MYSQL_CLIENT_PLUGIN_AUTH = (1UL << 19), /* Client supports plugin authentication. */
189     SW_MYSQL_CLIENT_CONNECT_ATTRS = (1UL << 20), /* Client supports connection attributes. */
190     SW_MYSQL_CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = (1UL << 21), /* Enable authentication response packet to be larger than 255 bytes. */
191     SW_MYSQL_CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = (1UL << 22), /* Don't close the connection for a user account with expired password. */
192     SW_MYSQL_CLIENT_SESSION_TRACK = (1UL << 23), /* Capable of handling server state change information. */
193     SW_MYSQL_CLIENT_DEPRECATE_EOF = (1UL << 24), /* Client no longer needs EOF_Packet and will use OK_Packet instead. */
194     SW_MYSQL_CLIENT_SSL_VERIFY_SERVER_CERT = (1UL << 30), /* Verify server certificate. */
195     SW_MYSQL_CLIENT_REMEMBER_OPTIONS = (1UL << 31) /* Don't reset the options after an unsuccessful connect. */
196 };
197 
198 // ref: https://dev.mysql.com/doc/internals/en/status-flags.html
199 enum sw_mysql_server_status_flags
200 {
201     SW_MYSQL_SERVER_STATUS_IN_TRANS = 0x0001, // a transaction is active
202     SW_MYSQL_SERVER_STATUS_AUTOCOMMIT = 0x0002, //auto-commit is enabled
203     SW_MYSQL_SERVER_MORE_RESULTS_EXISTS = 0x0008,
204     SW_MYSQL_SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010,
205     SW_MYSQL_SERVER_STATUS_NO_INDEX_USED = 0x0020,
206     SW_MYSQL_SERVER_STATUS_CURSOR_EXISTS = 0x0040, // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH must be used to fetch the row-data.
207     SW_MYSQL_SERVER_STATUS_LAST_ROW_SENT = 0x0080,
208     SW_MYSQL_SERVER_STATUS_DB_DROPPED = 0x0100,
209     SW_MYSQL_SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200,
210     SW_MYSQL_SERVER_STATUS_METADATA_CHANGED = 0x0400,
211     SW_MYSQL_SERVER_QUERY_WAS_SLOW = 0x0800,
212     SW_MYSQL_SERVER_PS_OUT_PARAMS = 0x1000,
213     SW_MYSQL_SERVER_STATUS_IN_TRANS_READONLY = 0x2000, // in a read-only transaction
214     SW_MYSQL_SERVER_SESSION_STATE_CHANGED = 0x4000 // connection state information has changed
215 };
216 
217 #define SW_MYSQL_NO_RSA_ERROR "MySQL8 caching_sha2_password authentication plugin need enable OpenSSL support"
218 
219 #define SW_MYSQL_NOT_NULL_FLAG               1
220 #define SW_MYSQL_PRI_KEY_FLAG                2
221 #define SW_MYSQL_UNIQUE_KEY_FLAG             4
222 #define SW_MYSQL_MULTIPLE_KEY_FLAG           8
223 #define SW_MYSQL_BLOB_FLAG                  16
224 #define SW_MYSQL_UNSIGNED_FLAG              32
225 #define SW_MYSQL_ZEROFILL_FLAG              64
226 #define SW_MYSQL_BINARY_FLAG               128
227 #define SW_MYSQL_ENUM_FLAG                 256
228 #define SW_MYSQL_AUTO_INCREMENT_FLAG       512
229 #define SW_MYSQL_TIMESTAMP_FLAG           1024
230 #define SW_MYSQL_SET_FLAG                 2048
231 #define SW_MYSQL_NO_DEFAULT_VALUE_FLAG    4096
232 #define SW_MYSQL_ON_UPDATE_NOW_FLAG       8192
233 #define SW_MYSQL_PART_KEY_FLAG           16384
234 #define SW_MYSQL_GROUP_FLAG              32768
235 #define SW_MYSQL_NUM_FLAG                32768
236 
237 /* int<3>   payload_length + int<1> sequence_id */
238 #define SW_MYSQL_PACKET_HEADER_SIZE      4
239 #define SW_MYSQL_PACKET_TYPE_OFFSET      5
240 #define SW_MYSQL_PACKET_EOF_MAX_SIZE     9
241 #define SW_MYSQL_PACKET_PREPARED_OK_SIZE 12
242 #define SW_MYSQL_MAX_PACKET_BODY_SIZE    0x00ffffff
243 #define SW_MYSQL_MAX_PACKET_SIZE         (SW_MYSQL_PACKET_HEADER_SIZE + SW_MYSQL_MAX_PACKET_BODY_SIZE)
244 
245 // nonce: a number or bit string used only once, in security engineering
246 // other names on doc: challenge/scramble/salt
247 #define SW_MYSQL_NONCE_LENGTH 20
248 
249 // clang-format off
250 #define sw_mysql_uint2korr2korr(A)  (uint16_t) (((uint16_t) ((uchar) (A)[0])) +\
251                                ((uint16_t) ((uchar) (A)[1]) << 8))
252 #define sw_mysql_uint2korr3korr(A)  (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\
253                                (((uint32_t) ((uchar) (A)[1])) << 8) +\
254                                (((uint32_t) ((uchar) (A)[2])) << 16))
255 #define sw_mysql_uint2korr4korr(A)  (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\
256                                (((uint32_t) ((uchar) (A)[1])) << 8) +\
257                                (((uint32_t) ((uchar) (A)[2])) << 16) +\
258                                (((uint32_t) ((uchar) (A)[3])) << 24))
259 #define sw_mysql_uint2korr8korr(A)    ((uint64_t)(((uint32_t) ((uchar) (A)[0])) +\
260                                     (((uint32_t) ((uchar) (A)[1])) << 8) +\
261                                     (((uint32_t) ((uchar) (A)[2])) << 16) +\
262                                     (((uint32_t) ((uchar) (A)[3])) << 24)) +\
263                                     (((uint64_t) (((uint32_t) ((uchar) (A)[4])) +\
264                                     (((uint32_t) ((uchar) (A)[5])) << 8) +\
265                                     (((uint32_t) ((uchar) (A)[6])) << 16) +\
266                                     (((uint32_t) ((uchar) (A)[7])) << 24))) << 32))
267 
268 #define sw_mysql_int1store(T,A)  do { *((int8_t*) (T)) = (int8_t)(A); } while(0)
269 #define sw_mysql_int2store(T,A)  do { uint32_t def_temp= (uint32_t) (A) ;\
270                   *((uchar*) (T))  =  (uchar)(def_temp); \
271                   *((uchar*) (T+1)) = (uchar)((def_temp >> 8)); } while (0)
272 #define sw_mysql_int3store(T,A)  do { /*lint -save -e734 */\
273                   *(((char *)(T)))   = (char) ((A));\
274                   *(((char *)(T))+1) = (char) (((A) >> 8));\
275                   *(((char *)(T))+2) = (char) (((A) >> 16)); \
276                   /*lint -restore */} while (0)
277 #define sw_mysql_int4store(T,A)  do { \
278                   *(((char *)(T)))   = (char) ((A));\
279                   *(((char *)(T))+1) = (char) (((A) >> 8));\
280                   *(((char *)(T))+2) = (char) (((A) >> 16));\
281                   *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0)
282 #define sw_mysql_int5store(T,A)  do { \
283                   *(((char *)(T)))   = (char)((A));\
284                   *(((char *)(T))+1) = (char)(((A) >> 8));\
285                   *(((char *)(T))+2) = (char)(((A) >> 16));\
286                   *(((char *)(T))+3) = (char)(((A) >> 24)); \
287                   *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0)
288 /* Based on int5store() from Andrey Hristov */
289 #define sw_mysql_int6store(T,A)  do { \
290                   *(((char *)(T)))   = (char)((A));\
291                   *(((char *)(T))+1) = (char)(((A) >> 8));\
292                   *(((char *)(T))+2) = (char)(((A) >> 16));\
293                   *(((char *)(T))+3) = (char)(((A) >> 24)); \
294                   *(((char *)(T))+4) = (char)(((A) >> 32)); \
295                   *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0)
296 
297 // clang-format on
298 
299 #define sw_mysql_int8store(T,A)  do { \
300                 uint32_t def_temp= (uint32_t) (A), def_temp2= (uint32_t) ((A) >> 32); \
301                 sw_mysql_int4store((T),def_temp); \
302                 sw_mysql_int4store((T+4),def_temp2); } while (0)
303 
304 #define sw_mysql_doublestore(T,A) do { \
305                 double def_temp = (double) A; \
306                 memcpy(T, &def_temp, sizeof(double)); \
307                 } while (0)
308 
309 #if defined(SW_DEBUG) && defined(SW_LOG_TRACE_OPEN)
310 #define swMysqlPacketDump(length, number, data, title) \
311     if (SW_LOG_TRACE >= sw_logger()->get_level() && (SW_TRACE_MYSQL_CLIENT & SwooleG.trace_flags)) \
312     { \
313         swoole_debug("+----------+------------+-------------------------------------------------------+"); \
314         swoole_debug("| P#%-6u | L%-9u | %-10u %42s |", number, SW_MYSQL_PACKET_HEADER_SIZE + length, length, title); \
315         swoole_hex_dump(data, length); \
316     }
317 #else
318 #define swMysqlPacketDump(length, number, data, title)
319 #endif
320 
321 namespace swoole { namespace mysql {
322 //-----------------------------------namespace begin--------------------------------------------
323 char get_charset(const char *name);
324 uint8_t get_static_type_size(uint8_t type);
325 
read_lcb_size(const char * p)326 inline uint8_t read_lcb_size(const char *p)
327 {
328     switch ((uchar) p[0])
329     {
330     case 251:
331         return 1;
332     case 252:
333         return 3;
334     case 253:
335         return 4;
336     case 254:
337         return 9;
338     default:
339         return 1;
340     }
341 }
342 
read_lcb(const char * p,uint64_t * length,bool * nul)343 inline uint8_t read_lcb(const char *p, uint64_t *length, bool *nul)
344 {
345     switch ((uchar) p[0])
346     {
347     case 251: /* fb : 1 octet */
348         *length = 0;
349         *nul = true;
350         return 1;
351     case 252: /* fc : 2 octets */
352         *length = sw_mysql_uint2korr2korr(p + 1);
353         *nul = false;
354         return 3;
355     case 253: /* fd : 3 octets */
356         *length = sw_mysql_uint2korr3korr(p + 1);
357         *nul = false;
358         return 4;
359     case 254: /* fe : 8 octets */
360         *length = sw_mysql_uint2korr8korr(p + 1);
361         *nul = false;
362         return 9;
363     default:
364         *length = (uchar) p[0];
365         *nul = false;
366         return 1;
367     }
368 }
369 
read_lcb(const char * p,uint32_t * length,bool * nul)370 inline uint8_t read_lcb(const char *p, uint32_t *length, bool *nul)
371 {
372     uint64_t _r;
373     uint8_t ret = read_lcb(p, &_r, nul);
374     *length = _r;
375     return ret;
376 }
377 
378 inline uint8_t write_lcb(char *p, uint64_t length, bool nul = false)
379 {
380     if (nul)
381     {
382         sw_mysql_int1store(p++, 251);
383         return 1;
384     }
385     if (length <= 250)
386     {
387         sw_mysql_int1store(p, length);
388         return 1;
389     }
390     else if (length <= 0xffff)
391     {
392         sw_mysql_int1store(p++, 252);
393         sw_mysql_int2store(p, length);
394         return 3;
395     }
396     else if (length <= 0xffffff)
397     {
398         sw_mysql_int1store(p++, 253);
399         sw_mysql_int3store(p, length);
400         return 4;
401     }
402     else
403     {
404         sw_mysql_int1store(p++, 254);
405         sw_mysql_int8store(p, length);
406         return 9;
407     }
408 }
409 
410 class packet
411 {
412 public:
get_length(const char * data)413     static inline uint32_t get_length(const char *data)
414     {
415         return sw_mysql_uint2korr3korr(data);
416     }
get_number(const char * data)417     static inline uint32_t get_number(const char *data)
418     {
419         return (uint8_t) data[3];
420     }
set_length(char * buffer,uint32_t length)421     static inline void set_length(char *buffer, uint32_t length)
422     {
423         buffer[0] = length;
424         buffer[1] = length >> 8;
425         buffer[2] = length >> 16;
426     }
set_number(char * buffer,uint8_t number)427     static inline void set_number(char *buffer, uint8_t number)
428     {
429         buffer[3] = number;
430     }
set_header(char * buffer,uint32_t length,uint8_t number)431     static inline void set_header(char *buffer, uint32_t length, uint8_t number)
432     {
433         set_length(buffer, length);
434         set_number(buffer, number);
435     }
436 };
437 
438 class server_packet : public packet
439 {
440 public:
441     struct header {
442         uint32_t length :24;
443         uint32_t number :8;
headerheader444         header() : length(0), number(0) { }
445     } header;
server_packet()446     server_packet() { }
server_packet(const char * data)447     server_packet(const char *data)
448     {
449         parse(data);
450     }
parse(const char * data)451     inline void parse(const char *data)
452     {
453         header.length = packet::get_length(data);
454         header.number = packet::get_number(data);
455     }
parse_type(const char * data)456     static inline uint8_t parse_type(const char *data)
457     {
458         if (sw_unlikely(!data))
459         {
460             return SW_MYSQL_PACKET_NULL;
461         }
462         return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE];
463     }
is_eof(const char * data)464     static inline bool is_eof(const char *data)
465     {
466         return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_EOF;
467     }
is_ok(const char * data)468     static inline bool is_ok(const char *data)
469     {
470         return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_OK;
471     }
is_err(const char * data)472     static inline bool is_err(const char *data)
473     {
474         return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_ERR;
475     }
476 };
477 
478 class server_status
479 {
480 public:
481     int16_t status = 0;
482     void operator =(uint16_t status)
483     {
484         this->status = status;
485     }
more_results_exists()486     inline bool more_results_exists()
487     {
488         bool b = !!(status & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS);
489         swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "More results exist = %u", b);
490         return b;
491     }
492 };
493 
494 class client_packet : public packet
495 {
496 public:
497     client_packet(size_t body_size = 1024 - SW_MYSQL_PACKET_HEADER_SIZE)
498     {
499         SW_ASSERT(body_size > 0);
500         if (body_size <= 4)
501         {
502             data.header = stack_buffer;
503         }
504         else
505         {
506             data.header = new char[SW_MEM_ALIGNED_SIZE(SW_MYSQL_PACKET_HEADER_SIZE + body_size)]();
507         }
508         data.body = data.header + SW_MYSQL_PACKET_HEADER_SIZE;
509     }
get_data()510     inline const char* get_data()
511     {
512         return data.header;
513     }
get_data_length()514     inline uint32_t get_data_length()
515     {
516         return SW_MYSQL_PACKET_HEADER_SIZE + get_length();
517     }
get_length()518     inline uint32_t get_length()
519     {
520         return sw_mysql_uint2korr3korr(data.header);
521     }
get_number()522     inline uint8_t get_number()
523     {
524         return (uint8_t) data.header[3];
525     }
get_body()526     inline const char* get_body()
527     {
528         return data.body;
529     }
set_header(uint32_t length,uint8_t number)530     inline void set_header(uint32_t length, uint8_t number)
531     {
532         packet::set_header(data.header, length, number);
533     }
~client_packet()534     ~client_packet()
535     {
536         if (data.header != stack_buffer)
537         {
538             delete[] data.header;
539         }
540     }
541 protected:
542     struct {
543         char *header = nullptr;
544         char *body = nullptr;
545     } data;
546     char stack_buffer[SW_MYSQL_PACKET_HEADER_SIZE + 4] = {};
547 };
548 
549 class command_packet : public client_packet
550 {
551 public:
552     command_packet(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0) : client_packet(1 + length)
553     {
554         set_command(command);
555         set_header(1 + length, 0);
556         if (length > 0)
557         {
558             memcpy(data.body + 1, sql, length);
559         }
560     };
set_command(enum sw_mysql_command command)561     inline void set_command(enum sw_mysql_command command)
562     {
563         data.body[0] = (char) command;
564     }
565 };
566 
567 class err_packet : public server_packet
568 {
569 public:
570     uint16_t code;
571     std::string msg;
572     char sql_state[5 + 1];
573     err_packet(const char *data);
574 };
575 
576 class ok_packet : public server_packet
577 {
578 public:
579     uint64_t affected_rows = 0;
580     uint64_t last_insert_id = 0;
581     mysql::server_status server_status;
582     unsigned int warning_count = 0;
ok_packet()583     ok_packet() { }
584     ok_packet(const char *data);
585 };
586 
587 class eof_packet : public server_packet
588 {
589 public:
590     uint16_t warning_count;
591     mysql::server_status server_status;
592     eof_packet(const char *data);
593 };
594 
595 class raw_data_packet : public server_packet
596 {
597 public:
598     const char *body;
raw_data_packet(const char * data)599     raw_data_packet(const char *data) : server_packet(data), body(data + SW_MYSQL_PACKET_HEADER_SIZE)
600     {
601         swMysqlPacketDump(header.length, header.number, data, "Protocol::RawData");
602     }
603 };
604 
605 class greeting_packet : public server_packet
606 {
607 public:
608     uint8_t protocol_version = 0;
609     std::string server_version = "";
610     int connection_id = 0;
611     char auth_plugin_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; // nonce + '\0'
612     uint8_t auth_plugin_data_length = 0;
613     char filler = 0;
614     int capability_flags = 0;
615     char charset = SW_MYSQL_DEFAULT_CHARSET;
616     mysql::server_status status_flags;
617     char reserved[10] = {};
618     std::string auth_plugin_name = "";
619     greeting_packet(const char *data);
620 };
621 
622 class login_packet : public client_packet
623 {
624 public:
625     login_packet(
626         greeting_packet *greeting_packet,
627         const std::string &user,
628         const std::string &password,
629         std::string database,
630         char charset
631     );
632 };
633 
634 class auth_switch_request_packet : public server_packet
635 {
636 public:
637     std::string auth_method_name = "mysql_native_password";
638     char auth_method_data[SW_MYSQL_NONCE_LENGTH + 1] = {};
639     auth_switch_request_packet(const char *data);
640 };
641 
642 class auth_switch_response_packet : public client_packet
643 {
644 public:
645     auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password);
646 };
647 
648 class auth_signature_request_packet : public server_packet
649 {
650 public:
651     char data[2] = {};
auth_signature_request_packet(const char * data)652     auth_signature_request_packet(const char *data) :server_packet(data)
653     {
654         swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSignatureRequest");
655         memcpy(&this->data, data + SW_MYSQL_PACKET_HEADER_SIZE, 2);
656     }
is_full_auth_required()657     inline bool is_full_auth_required()
658     {
659         return data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED;
660     }
is_vaild()661     inline bool is_vaild()
662     {
663         return data[0] == SW_MYSQL_AUTH_SIGNATURE && (data[1] == SW_MYSQL_AUTH_SIGNATURE_SUCCESS || data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED);
664     }
665 };
666 
667 class auth_signature_prepared_packet : public client_packet
668 {
669 public:
auth_signature_prepared_packet(uint8_t number)670     auth_signature_prepared_packet(uint8_t number) : client_packet(1)
671     {
672         set_header(1, number);
673         data.body[0] = SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED;
674     }
675 };
676 
677 class auth_signature_response_packet : public client_packet
678 {
679 public:
680     auth_signature_response_packet(raw_data_packet *raw_data_pakcet, const std::string &password, const char *auth_plugin_data);
681 };
682 
683 class lcb_packet : public server_packet
684 {
685 public:
686     uint32_t length = 0;
687     bool nul = 0;
lcb_packet(const char * data)688     lcb_packet(const char *data) : server_packet(data)
689     {
690         swMysqlPacketDump(header.length, header.number, data, "Protocol::LengthCodedBinary");
691         bytes_length = read_lcb(data + SW_MYSQL_PACKET_HEADER_SIZE, &length, &nul);
692         swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "binary_length=%u, nul=%u", header.length, nul);
693     }
is_vaild()694     bool is_vaild()
695     {
696         return header.length == bytes_length;
697     }
698 private:
699     uint8_t bytes_length;
700 };
701 
702 class field_packet : public server_packet
703 {
704 public:
705     char *catalog = nullptr; /* Catalog for table */
706     uint32_t catalog_length = 0;
707     char *database = nullptr; /* Database for table */
708     uint32_t database_length = 0;
709     char *table = nullptr; /* Table of column if column was a field */
710     uint32_t table_length = 0;
711     char *org_table = nullptr; /* Org table name, if table was an alias */
712     uint32_t org_table_length = 0;
713     char *name = nullptr; /* Name of column */
714     uint32_t name_length = 0;
715     char *org_name = nullptr; /* Original column name, if an alias */
716     uint32_t org_name_length = 0;
717     char charset = 0;
718     uint64_t length = 0; /* Width of column (create length) */
719     uint8_t type = 0; /* Type of field. See mysql_com.h for types */
720     uint32_t flags = 0; /* Div flags */
721     uint32_t decimals = 0; /* Number of decimals in field */
722     char *def = nullptr; /* Default value (set by mysql_list_fields) */
723     uint32_t def_length = 0;
724     void *extension = nullptr;
field_packet()725     field_packet() { }
field_packet(const char * data)726     field_packet(const char *data) {
727         parse(data);
728     }
729     void parse(const char *data);
~field_packet()730     ~field_packet()
731     {
732         if (body)
733         {
734             delete[] body;
735         }
736     }
737 protected:
738     char *body = nullptr;
739 };
740 
741 typedef field_packet param_packet;
742 
743 class row_data
744 {
745 public:
746     char stack_buffer[32];
747     struct {
748         uint64_t length; // binary code length
749         bool nul; // is nul?
750     } text;
row_data(const char * data)751     row_data(const char *data)
752     {
753         next_packet(data);
754     }
next_packet(const char * data)755     inline void next_packet(const char *data)
756     {
757         read_ptr = packet_body = data + SW_MYSQL_PACKET_HEADER_SIZE;
758         packet_eof = packet_body + packet::get_length(data);
759     }
eof()760     inline bool eof()
761     {
762         return read_ptr == packet_eof;
763     }
read(size_t length)764     inline const char* read(size_t length)
765     {
766         if (sw_likely(read_ptr + length <= packet_eof))
767         {
768             const char *p = read_ptr;
769             read_ptr += length;
770             return p;
771         }
772         return nullptr;
773     }
recv(char * buf,size_t size)774     inline uint32_t recv(char *buf, size_t size)
775     {
776         uint32_t readable_length = packet_eof - read_ptr;
777         uint32_t read_bytes = SW_MIN(readable_length, size);
778         if (sw_likely(read_bytes > 0))
779         {
780             memcpy(buf, read_ptr, read_bytes);
781             read_ptr += read_bytes;
782         }
783         return read_bytes;
784     }
785 protected:
786     const char *packet_body;
787     const char *packet_eof;
788     const char *read_ptr;
789 };
790 
791 class row_data_text
792 {
793 public:
794     uint64_t length = 0;
795     bool nul = false;
796     const char *body = nullptr;
row_data_text(const char ** pp)797     row_data_text(const char **pp)
798     {
799         body = *pp + read_lcb(*pp, &length, &nul);
800         *pp = body + length;
801         swoole_trace_log(
802             SW_TRACE_MYSQL_CLIENT,
803             "text[%" PRIu64 "]: %.*s%s",
804             length, (int) SW_MIN(64, length), body,
805             nul ? "null" : ((length > 64 /*|| length > readable_length*/) ? "..." : "")
806         );
807     }
808 };
809 
datetime(const char * p,uint8_t length,uint32_t decimals)810 inline std::string datetime(const char *p, uint8_t length, uint32_t decimals)
811 {
812     uint16_t y = 0;
813     uint8_t m = 0, d = 0, h = 0, i = 0, s = 0;
814     uint32_t sp = 0;
815     if (length != 0)
816     {
817         y = sw_mysql_uint2korr2korr(p);
818         m = *(uint8_t *) (p + 2);
819         d = *(uint8_t *) (p + 3);
820         if (length > 4)
821         {
822             h = *(uint8_t *) (p + 4);
823             i = *(uint8_t *) (p + 5);
824             s = *(uint8_t *) (p + 6);
825         }
826         if (length > 7)
827         {
828             sp = sw_mysql_uint2korr4korr(p + 7);
829         }
830     }
831     if (decimals > 0 && decimals < 7) {
832         return swoole::std_string::format(
833             "%04u-%02u-%02u %02u:%02u:%02u.%0*u",
834             y, m, d, h, i, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals)))
835         );
836     } else {
837         return swoole::std_string::format(
838             "%04u-%02u-%02u %02u:%02u:%02u",
839             y, m, d, h, i, s
840         );
841     }
842 }
843 
time(const char * p,uint8_t length,uint32_t decimals)844 inline std::string time(const char *p, uint8_t length, uint32_t decimals)
845 {
846     bool neg = false;
847     uint32_t d = 0, sp = 0;
848     uint8_t h = 0, m = 0, s = 0;
849     if (length != 0)
850     {
851         neg = (bool) *((uint8_t *) p);
852         d = sw_mysql_uint2korr4korr(p + 1);
853         h = *(uint8_t *) (p + 5);
854         m = *(uint8_t *) (p + 6);
855         s = *(uint8_t *) (p + 7);
856         if (length > 8)
857         {
858             sp = sw_mysql_uint2korr4korr(p + 8);
859         }
860         if (d != 0) {
861             /* Convert days to hours at once */
862             h += d * 24;
863         }
864     }
865     if (decimals > 0 && decimals < 7) {
866         return swoole::std_string::format(
867             "%s%02u:%02u:%02u.%0*u",
868             (neg ? "-" : ""), h, m, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals)))
869         );
870     } else {
871         return swoole::std_string::format(
872             "%s%02u:%02u:%02u",
873             (neg ? "-" : ""), h, m, s
874         );
875     }
876 }
877 
date(const char * p,uint8_t length)878 inline std::string date(const char *p, uint8_t length)
879 {
880     uint16_t y = 0;
881     uint8_t m = 0, d = 0;
882     if (length != 0)
883     {
884         y = sw_mysql_uint2korr2korr(p);
885         m = *(uint8_t *) (p + 2);
886         d = *(uint8_t *) (p + 3);
887     }
888     return swoole::std_string::format("%04u-%02u-%02u", y, m, d);
889 }
890 
891 class result_info
892 {
893 public:
894     ok_packet ok;
895 
alloc_fields(uint32_t length)896     inline void alloc_fields(uint32_t length)
897     {
898         clear_fields();
899         if (sw_likely(length != 0))
900         {
901             fields.info = new field_packet[length];
902             fields.length = length;
903         }
904         else
905         {
906             fields.length = 0;
907             fields.info = nullptr;
908         }
909     }
get_fields_length()910     inline uint32_t get_fields_length()
911     {
912         return fields.length;
913     }
get_fields(uint32_t index)914     inline field_packet* get_fields(uint32_t index)
915     {
916         return fields.info;
917     }
get_field(uint32_t index)918     inline field_packet* get_field(uint32_t index)
919     {
920         return &fields.info[index];
921     }
set_field(uint32_t index,const char * data)922     inline void set_field(uint32_t index, const char *data)
923     {
924         fields.info[index].parse(data);
925     }
clear_fields()926     inline void clear_fields()
927     {
928         if (fields.length > 0)
929         {
930             delete[] fields.info;
931         }
932     }
~result_info()933     ~result_info()
934     {
935         clear_fields();
936     }
937 protected:
938     struct {
939         uint32_t length = 0;
940         field_packet *info = nullptr;
941     } fields;
942 };
943 
944 class statement : public server_packet
945 {
946 public:
947     uint32_t id = 0;
948     uint16_t field_count = 0;
949     uint16_t param_count = 0;
950     uint16_t warning_count = 0;
statement()951     statement() { }
statement(const char * data)952     statement(const char* data) : server_packet(data)
953     {
954         swMysqlPacketDump(header.length, header.number, data, "COM_STMT_PREPARE_OK_Packet");
955         // skip the packet header
956         data += SW_MYSQL_PACKET_HEADER_SIZE;
957         // status (1) -- [00] OK
958         SW_ASSERT(data[0] == SW_MYSQL_PACKET_OK);
959         data += 1;
960         // statement_id (4) -- statement-id
961         id = sw_mysql_uint2korr4korr(data);
962         data += 4;
963         // num_columns (2) -- number of columns
964         field_count = sw_mysql_uint2korr2korr(data);
965         data += 2;
966         // num_params (2) -- number of params
967         param_count = sw_mysql_uint2korr2korr(data);
968         data += 2;
969         // reserved_1 (1) -- [00] filler
970         data += 1;
971         // warning_count (2) -- number of warnings
972         warning_count = sw_mysql_uint2korr2korr(data);
973         swoole_trace_log(
974             SW_TRACE_MYSQL_CLIENT, "statement_id=%u, field_count=%u, param_count=%u, warning_count=%u",
975             id, field_count, param_count, warning_count
976         );
977     }
978 };
979 
980 class null_bitmap
981 {
982 public:
get_size(uint32_t field_length)983     static uint32_t get_size(uint32_t field_length)
984     {
985         return ((field_length + 9) / 8) + 1;
986     }
null_bitmap(const char * p,uint32_t size)987     null_bitmap(const char *p, uint32_t size) :
988             size(size)
989     {
990         map = new char[size];
991         memcpy(map, p, size);
992         swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "null_count=%u", size);
993     }
is_null(size_t i)994     inline bool is_null(size_t i)
995     {
996         return ((map + 1)[((i + 2) / 8)] & (0x01 << ((i + 2) % 8))) != 0;
997     }
~null_bitmap()998     ~null_bitmap()
999     {
1000         delete[] map;
1001     }
1002 protected:
1003     uint32_t size;
1004     char *map;
1005 };
1006 //-----------------------------------namespace end--------------------------------------------
1007 }}
1008