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