1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP 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 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | 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 | Authors: Andrey Hristov <andrey@php.net> |
14 | Ulf Wendel <uw@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #include "php.h"
19 #include "mysqlnd.h"
20 #include "mysqlnd_connection.h"
21 #include "mysqlnd_ps.h"
22 #include "mysqlnd_priv.h"
23 #include "mysqlnd_wireprotocol.h"
24 #include "mysqlnd_statistics.h"
25 #include "mysqlnd_debug.h"
26
27 #define BAIL_IF_NO_MORE_DATA \
28 if (UNEXPECTED((size_t)(p - begin) > packet->header.size)) { \
29 php_error_docref(NULL, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \
30 goto premature_end; \
31 } \
32
33
34 static const char *unknown_sqlstate= "HY000";
35 const char * const mysqlnd_empty_string = "";
36
37 /* Used in mysqlnd_debug.c */
38 const char mysqlnd_read_header_name[] = "mysqlnd_read_header";
39 const char mysqlnd_read_body_name[] = "mysqlnd_read_body";
40
41 #define ERROR_MARKER 0xFF
42 #define EODATA_MARKER 0xFE
43
44 #define MARIADB_RPL_VERSION_HACK "5.5.5-"
45
46 /* {{{ mysqlnd_command_to_text */
47 const char * const mysqlnd_command_to_text[COM_END] =
48 {
49 "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
50 "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
51 "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
52 "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
53 "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
54 "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
55 "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON", "BINLOG_DUMP_GTID",
56 "RESET_CONNECTION"
57 };
58 /* }}} */
59
60
61
62 static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
63 {
64 STAT_LAST,
65 STAT_LAST,
66 STAT_BYTES_RECEIVED_OK,
67 STAT_BYTES_RECEIVED_EOF,
68 STAT_LAST,
69 STAT_BYTES_RECEIVED_RSET_HEADER,
70 STAT_BYTES_RECEIVED_RSET_FIELD_META,
71 STAT_BYTES_RECEIVED_RSET_ROW,
72 STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
73 STAT_BYTES_RECEIVED_CHANGE_USER,
74 };
75
76 static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
77 {
78 STAT_LAST,
79 STAT_LAST,
80 STAT_PACKETS_RECEIVED_OK,
81 STAT_PACKETS_RECEIVED_EOF,
82 STAT_LAST,
83 STAT_PACKETS_RECEIVED_RSET_HEADER,
84 STAT_PACKETS_RECEIVED_RSET_FIELD_META,
85 STAT_PACKETS_RECEIVED_RSET_ROW,
86 STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
87 STAT_PACKETS_RECEIVED_CHANGE_USER,
88 };
89
90
91 /* {{{ php_mysqlnd_net_field_length
92 Get next field's length */
93 zend_ulong
php_mysqlnd_net_field_length(const zend_uchar ** packet)94 php_mysqlnd_net_field_length(const zend_uchar **packet)
95 {
96 const zend_uchar *p= (const zend_uchar *)*packet;
97
98 if (*p < 251) {
99 (*packet)++;
100 return (zend_ulong) *p;
101 }
102
103 switch (*p) {
104 case 251:
105 (*packet)++;
106 return MYSQLND_NULL_LENGTH;
107 case 252:
108 (*packet) += 3;
109 return (zend_ulong) uint2korr(p+1);
110 case 253:
111 (*packet) += 4;
112 return (zend_ulong) uint3korr(p+1);
113 default:
114 (*packet) += 9;
115 return (zend_ulong) uint4korr(p+1);
116 }
117 }
118 /* }}} */
119
120
121 /* {{{ php_mysqlnd_net_field_length_ll
122 Get next field's length */
123 uint64_t
php_mysqlnd_net_field_length_ll(const zend_uchar ** packet)124 php_mysqlnd_net_field_length_ll(const zend_uchar **packet)
125 {
126 const zend_uchar *p = (zend_uchar *)*packet;
127
128 if (*p < 251) {
129 (*packet)++;
130 return (uint64_t) *p;
131 }
132
133 switch (*p) {
134 case 251:
135 (*packet)++;
136 return (uint64_t) MYSQLND_NULL_LENGTH;
137 case 252:
138 (*packet) += 3;
139 return (uint64_t) uint2korr(p + 1);
140 case 253:
141 (*packet) += 4;
142 return (uint64_t) uint3korr(p + 1);
143 default:
144 (*packet) += 9;
145 return (uint64_t) uint8korr(p + 1);
146 }
147 }
148 /* }}} */
149
150
151 /* {{{ php_mysqlnd_net_store_length */
152 zend_uchar *
php_mysqlnd_net_store_length(zend_uchar * packet,const uint64_t length)153 php_mysqlnd_net_store_length(zend_uchar *packet, const uint64_t length)
154 {
155 if (length < (uint64_t) L64(251)) {
156 *packet = (zend_uchar) length;
157 return packet + 1;
158 }
159
160 if (length < (uint64_t) L64(65536)) {
161 *packet++ = 252;
162 int2store(packet,(unsigned int) length);
163 return packet + 2;
164 }
165
166 if (length < (uint64_t) L64(16777216)) {
167 *packet++ = 253;
168 int3store(packet,(zend_ulong) length);
169 return packet + 3;
170 }
171 *packet++ = 254;
172 int8store(packet, length);
173 return packet + 8;
174 }
175 /* }}} */
176
177
178 /* {{{ php_mysqlnd_net_store_length_size */
179 size_t
php_mysqlnd_net_store_length_size(uint64_t length)180 php_mysqlnd_net_store_length_size(uint64_t length)
181 {
182 if (length < (uint64_t) L64(251)) {
183 return 1;
184 }
185 if (length < (uint64_t) L64(65536)) {
186 return 3;
187 }
188 if (length < (uint64_t) L64(16777216)) {
189 return 4;
190 }
191 return 9;
192 }
193 /* }}} */
194
195
196 /* {{{ php_mysqlnd_read_error_from_line */
197 static enum_func_status
php_mysqlnd_read_error_from_line(const zend_uchar * const buf,const size_t buf_len,char * error,const size_t error_buf_len,unsigned int * error_no,char * sqlstate)198 php_mysqlnd_read_error_from_line(const zend_uchar * const buf, const size_t buf_len,
199 char *error, const size_t error_buf_len,
200 unsigned int *error_no, char *sqlstate)
201 {
202 const zend_uchar *p = buf;
203 size_t error_msg_len = 0;
204
205 DBG_ENTER("php_mysqlnd_read_error_from_line");
206
207 *error_no = CR_UNKNOWN_ERROR;
208 memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
209
210 if (buf_len > 2) {
211 *error_no = uint2korr(p);
212 p+= 2;
213 /*
214 sqlstate is following. No need to check for buf_left_len as we checked > 2 above,
215 if it was >=2 then we would need a check
216 */
217 if (*p == '#') {
218 ++p;
219 if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) {
220 memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH);
221 p+= MYSQLND_SQLSTATE_LENGTH;
222 } else {
223 goto end;
224 }
225 }
226 if ((buf_len - (p - buf)) > 0) {
227 error_msg_len = MIN((int)((buf_len - (p - buf))), (int) (error_buf_len - 1));
228 memcpy(error, p, error_msg_len);
229 }
230 }
231 end:
232 sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
233 error[error_msg_len]= '\0';
234
235 DBG_RETURN(FAIL);
236 }
237 /* }}} */
238
239
240 /* {{{ mysqlnd_read_header */
241 static enum_func_status
mysqlnd_read_header(MYSQLND_PFC * pfc,MYSQLND_VIO * vio,MYSQLND_PACKET_HEADER * header,MYSQLND_STATS * conn_stats,MYSQLND_ERROR_INFO * error_info)242 mysqlnd_read_header(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, MYSQLND_PACKET_HEADER * header,
243 MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
244 {
245 zend_uchar buffer[MYSQLND_HEADER_SIZE];
246
247 DBG_ENTER(mysqlnd_read_header_name);
248 DBG_INF_FMT("compressed=%u", pfc->data->compressed);
249 if (FAIL == pfc->data->m.receive(pfc, vio, buffer, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
250 DBG_RETURN(FAIL);
251 }
252
253 header->size = uint3korr(buffer);
254 header->packet_no = uint1korr(buffer + 3);
255
256 DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3zu", header->packet_no, header->size);
257 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn_stats,
258 STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
259 STAT_PACKETS_RECEIVED, 1);
260
261 if (pfc->data->compressed || pfc->data->packet_no == header->packet_no) {
262 /*
263 Have to increase the number, so we can send correct number back. It will
264 round at 255 as this is unsigned char. The server needs this for simple
265 flow control checking.
266 */
267 pfc->data->packet_no++;
268 DBG_RETURN(PASS);
269 }
270 // @see https://dev.mysql.com/worklog/task/?id=12999
271 if (header->size > 0) {
272 zend_uchar *buf = mnd_emalloc(header->size);
273 if ((PASS == pfc->data->m.receive(pfc, vio, buf, header->size, conn_stats, error_info)) && buf[0] == ERROR_MARKER) {
274 php_mysqlnd_read_error_from_line(buf + 1, header->size - 1,
275 error_info->error, sizeof(error_info->error),
276 &error_info->error_no, error_info->sqlstate
277 );
278 mnd_efree(buf);
279 DBG_RETURN(FAIL);
280 }
281 mnd_efree(buf);
282 }
283
284 DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size=%zu",
285 pfc->data->packet_no, header->packet_no, header->size);
286
287 php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size=%zu",
288 pfc->data->packet_no, header->packet_no, header->size);
289 DBG_RETURN(FAIL);
290 }
291 /* }}} */
292
293
294 /* {{{ mysqlnd_read_packet_header_and_body */
295 static enum_func_status
mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,MYSQLND_PFC * pfc,MYSQLND_VIO * vio,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info,MYSQLND_CONNECTION_STATE * connection_state,zend_uchar * const buf,const size_t buf_size,const char * const packet_type_as_text,enum mysqlnd_packet_type packet_type)296 mysqlnd_read_packet_header_and_body(MYSQLND_PACKET_HEADER * packet_header,
297 MYSQLND_PFC * pfc,
298 MYSQLND_VIO * vio,
299 MYSQLND_STATS * stats,
300 MYSQLND_ERROR_INFO * error_info,
301 MYSQLND_CONNECTION_STATE * connection_state,
302 zend_uchar * const buf, const size_t buf_size,
303 const char * const packet_type_as_text,
304 enum mysqlnd_packet_type packet_type)
305 {
306 DBG_ENTER("mysqlnd_read_packet_header_and_body");
307 DBG_INF_FMT("buf=%p size=%zu", buf, buf_size);
308 if (FAIL == mysqlnd_read_header(pfc, vio, packet_header, stats, error_info)) {
309 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
310 if (error_info->error_no == 0) {
311 SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
312 }
313 DBG_ERR_FMT("Can't read %s's header", packet_type_as_text);
314 DBG_RETURN(FAIL);
315 }
316 if (buf_size < packet_header->size) {
317 DBG_ERR_FMT("Packet buffer %zu wasn't big enough %zu, %zu bytes will be unread",
318 buf_size, packet_header->size, packet_header->size - buf_size);
319 SET_CLIENT_ERROR(error_info, CR_INVALID_BUFFER_USE, UNKNOWN_SQLSTATE, "Packet buffer wasn't big enough; as a workaround consider increasing value of net_cmd_buffer_size");
320 DBG_RETURN(FAIL);
321 }
322 if (FAIL == pfc->data->m.receive(pfc, vio, buf, packet_header->size, stats, error_info)) {
323 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
324 SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
325 DBG_ERR_FMT("Empty '%s' packet body", packet_type_as_text);
326 DBG_RETURN(FAIL);
327 }
328 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, packet_type_to_statistic_byte_count[packet_type],
329 MYSQLND_HEADER_SIZE + packet_header->size,
330 packet_type_to_statistic_packet_count[packet_type],
331 1);
332 DBG_RETURN(PASS);
333 }
334 /* }}} */
335
336
337 /* {{{ php_mysqlnd_greet_read */
338 static enum_func_status
php_mysqlnd_greet_read(MYSQLND_CONN_DATA * conn,void * _packet)339 php_mysqlnd_greet_read(MYSQLND_CONN_DATA * conn, void * _packet)
340 {
341 const zend_uchar * pad_start = NULL;
342 MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
343 MYSQLND_ERROR_INFO * error_info = conn->error_info;
344 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
345 MYSQLND_VIO * vio = conn->vio;
346 MYSQLND_STATS * stats = conn->stats;
347 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
348 const size_t buf_len = pfc->cmd_buffer.length;
349 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
350 const zend_uchar * p = buf;
351 const zend_uchar * const begin = buf;
352
353 DBG_ENTER("php_mysqlnd_greet_read");
354
355 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "greeting", PROT_GREET_PACKET)) {
356 DBG_RETURN(FAIL);
357 }
358 BAIL_IF_NO_MORE_DATA;
359
360 packet->authentication_plugin_data.s = packet->intern_auth_plugin_data;
361 packet->authentication_plugin_data.l = sizeof(packet->intern_auth_plugin_data);
362
363 packet->protocol_version = uint1korr(p);
364 p++;
365 BAIL_IF_NO_MORE_DATA;
366
367 if (ERROR_MARKER == packet->protocol_version) {
368 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
369 packet->error, sizeof(packet->error),
370 &packet->error_no, packet->sqlstate
371 );
372 /*
373 The server doesn't send sqlstate in the greet packet.
374 It's a bug#26426 , so we have to set it correctly ourselves.
375 It's probably "Too many connections, which has SQL state 08004".
376 */
377 if (packet->error_no == 1040) {
378 memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
379 }
380 DBG_RETURN(PASS);
381 }
382
383 /* MariaDB always sends 5.5.5 before version string: 5.5.5 was never released,
384 so just ignore it */
385 if (!strncmp((char *) p, MARIADB_RPL_VERSION_HACK, sizeof(MARIADB_RPL_VERSION_HACK) - 1)) {
386 p += sizeof(MARIADB_RPL_VERSION_HACK) - 1;
387 }
388
389 packet->server_version = estrdup((char *)p);
390 p+= strlen(packet->server_version) + 1; /* eat the '\0' */
391 BAIL_IF_NO_MORE_DATA;
392
393 packet->thread_id = uint4korr(p);
394 p+=4;
395 BAIL_IF_NO_MORE_DATA;
396
397 memcpy(packet->authentication_plugin_data.s, p, SCRAMBLE_LENGTH_323);
398 p+= SCRAMBLE_LENGTH_323;
399 BAIL_IF_NO_MORE_DATA;
400
401 /* pad1 */
402 p++;
403 BAIL_IF_NO_MORE_DATA;
404
405 packet->server_capabilities = uint2korr(p);
406 p+= 2;
407 BAIL_IF_NO_MORE_DATA;
408 DBG_INF_FMT("4.1 server_caps=%u\n", (uint32_t) packet->server_capabilities);
409
410 packet->charset_no = uint1korr(p);
411 p++;
412 BAIL_IF_NO_MORE_DATA;
413
414 packet->server_status = uint2korr(p);
415 p+= 2;
416 BAIL_IF_NO_MORE_DATA;
417
418 /* pad2 */
419 pad_start = p;
420 p+= 13;
421 BAIL_IF_NO_MORE_DATA;
422
423 if ((size_t) (p - buf) < packet->header.size) {
424 /* auth_plugin_data is split into two parts */
425 memcpy(packet->authentication_plugin_data.s + SCRAMBLE_LENGTH_323, p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
426 p+= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323;
427 p++; /* 0x0 at the end of the scramble and thus last byte in the packet in 5.1 and previous */
428 } else {
429 packet->pre41 = TRUE;
430 }
431
432 /* Is this a 5.5+ server ? */
433 if ((size_t) (p - buf) < packet->header.size) {
434 /* backtrack one byte, the 0x0 at the end of the scramble in 5.1 and previous */
435 p--;
436
437 /* Additional 16 bits for server capabilities */
438 DBG_INF_FMT("additional 5.5+ caps=%u\n", (uint32_t) uint2korr(pad_start));
439 packet->server_capabilities |= ((uint32_t) uint2korr(pad_start)) << 16;
440 /* And a length of the server scramble in one byte */
441 packet->authentication_plugin_data.l = uint1korr(pad_start + 2);
442 if (packet->authentication_plugin_data.l > SCRAMBLE_LENGTH) {
443 /* more data*/
444 char * new_auth_plugin_data = emalloc(packet->authentication_plugin_data.l);
445
446 /* copy what we already have */
447 memcpy(new_auth_plugin_data, packet->authentication_plugin_data.s, SCRAMBLE_LENGTH);
448 /* add additional scramble data 5.5+ sent us */
449 memcpy(new_auth_plugin_data + SCRAMBLE_LENGTH, p, packet->authentication_plugin_data.l - SCRAMBLE_LENGTH);
450 p+= (packet->authentication_plugin_data.l - SCRAMBLE_LENGTH);
451 packet->authentication_plugin_data.s = new_auth_plugin_data;
452 }
453 }
454
455 if (packet->server_capabilities & CLIENT_PLUGIN_AUTH) {
456 BAIL_IF_NO_MORE_DATA;
457 /* The server is 5.5.x and supports authentication plugins */
458 packet->auth_protocol = estrdup((char *)p);
459 p+= strlen(packet->auth_protocol) + 1; /* eat the '\0' */
460 }
461
462 DBG_INF_FMT("proto=%u server=%s thread_id=%u",
463 packet->protocol_version, packet->server_version, packet->thread_id);
464
465 DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i auth_protocol=%s scramble_length=%zu",
466 packet->server_capabilities, packet->charset_no, packet->server_status,
467 packet->auth_protocol? packet->auth_protocol:"n/a", packet->authentication_plugin_data.l);
468
469 DBG_RETURN(PASS);
470 premature_end:
471 DBG_ERR_FMT("GREET packet %zu bytes shorter than expected", p - begin - packet->header.size);
472 php_error_docref(NULL, E_WARNING, "GREET packet %zu bytes shorter than expected",
473 p - begin - packet->header.size);
474 DBG_RETURN(FAIL);
475 }
476 /* }}} */
477
478
479 /* {{{ php_mysqlnd_greet_free_mem */
480 static
php_mysqlnd_greet_free_mem(void * _packet)481 void php_mysqlnd_greet_free_mem(void * _packet)
482 {
483 MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet;
484 if (p->server_version) {
485 efree(p->server_version);
486 p->server_version = NULL;
487 }
488 if (p->authentication_plugin_data.s && p->authentication_plugin_data.s != p->intern_auth_plugin_data) {
489 efree(p->authentication_plugin_data.s);
490 p->authentication_plugin_data.s = NULL;
491 }
492 if (p->auth_protocol) {
493 efree(p->auth_protocol);
494 p->auth_protocol = NULL;
495 }
496 }
497 /* }}} */
498
499
500 #define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 4096)
501
502 /* {{{ php_mysqlnd_auth_write */
503 static
php_mysqlnd_auth_write(MYSQLND_CONN_DATA * conn,void * _packet)504 size_t php_mysqlnd_auth_write(MYSQLND_CONN_DATA * conn, void * _packet)
505 {
506 zend_uchar buffer[AUTH_WRITE_BUFFER_LEN];
507 zend_uchar *p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
508 size_t len;
509 MYSQLND_PACKET_AUTH * packet= (MYSQLND_PACKET_AUTH *) _packet;
510 MYSQLND_ERROR_INFO * error_info = conn->error_info;
511 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
512 MYSQLND_VIO * vio = conn->vio;
513 MYSQLND_STATS * stats = conn->stats;
514 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
515
516 DBG_ENTER("php_mysqlnd_auth_write");
517
518 if (!packet->is_change_user_packet) {
519 int4store(p, packet->client_flags);
520 p+= 4;
521
522 int4store(p, packet->max_packet_size);
523 p+= 4;
524
525 int1store(p, packet->charset_no);
526 p++;
527
528 memset(p, 0, 23); /* filler */
529 p+= 23;
530 }
531
532 if (packet->send_auth_data || packet->is_change_user_packet) {
533 len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN);
534 p = zend_mempcpy(p, packet->user, len);
535 *p++ = '\0';
536
537 /* defensive coding */
538 if (packet->auth_data == NULL) {
539 packet->auth_data_len = 0;
540 }
541 if (packet->auth_data_len > 0xFF) {
542 const char * const msg = "Authentication data too long. "
543 "Won't fit into the buffer and will be truncated. Authentication will thus fail";
544 SET_CLIENT_ERROR(error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, msg);
545 php_error_docref(NULL, E_WARNING, "%s", msg);
546 DBG_RETURN(0);
547 }
548
549 int1store(p, (int8_t)packet->auth_data_len);
550 ++p;
551 /*!!!!! is the buffer big enough ??? */
552 if (sizeof(buffer) < (packet->auth_data_len + (p - buffer))) {
553 DBG_ERR("the stack buffer was not enough!!");
554 DBG_RETURN(0);
555 }
556 if (packet->auth_data_len) {
557 p = zend_mempcpy(p, packet->auth_data, packet->auth_data_len);
558 }
559
560 if (packet->db_len > 0) {
561 /* CLIENT_CONNECT_WITH_DB should have been set */
562 size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len);
563 p = zend_mempcpy(p, packet->db, real_db_len);
564 *p++= '\0';
565 } else if (packet->is_change_user_packet) {
566 *p++= '\0';
567 }
568 /* no \0 for no DB */
569
570 if (packet->is_change_user_packet) {
571 if (packet->charset_no) {
572 int2store(p, packet->charset_no);
573 p+= 2;
574 }
575 }
576
577 if (packet->auth_plugin_name) {
578 len = MIN(strlen(packet->auth_plugin_name), sizeof(buffer) - (p - buffer) - 1);
579 p = zend_mempcpy(p, packet->auth_plugin_name, len);
580 *p++= '\0';
581 }
582
583 if (packet->connect_attr && zend_hash_num_elements(packet->connect_attr)) {
584 size_t ca_payload_len = 0;
585
586 {
587 zend_string * key;
588 zval * entry_value;
589 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(packet->connect_attr, key, entry_value) {
590 if (key) { /* HASH_KEY_IS_STRING */
591 size_t value_len = Z_STRLEN_P(entry_value);
592
593 ca_payload_len += php_mysqlnd_net_store_length_size(ZSTR_LEN(key));
594 ca_payload_len += ZSTR_LEN(key);
595 ca_payload_len += php_mysqlnd_net_store_length_size(value_len);
596 ca_payload_len += value_len;
597 }
598 } ZEND_HASH_FOREACH_END();
599 }
600
601 if (sizeof(buffer) >= (ca_payload_len + php_mysqlnd_net_store_length_size(ca_payload_len) + (p - buffer))) {
602 p = php_mysqlnd_net_store_length(p, ca_payload_len);
603
604 {
605 zend_string * key;
606 zval * entry_value;
607 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(packet->connect_attr, key, entry_value) {
608 if (key) { /* HASH_KEY_IS_STRING */
609 size_t value_len = Z_STRLEN_P(entry_value);
610
611 /* copy key */
612 p = php_mysqlnd_net_store_length(p, ZSTR_LEN(key));
613 p = zend_mempcpy(p, ZSTR_VAL(key), ZSTR_LEN(key));
614 /* copy value */
615 p = php_mysqlnd_net_store_length(p, value_len);
616 p = zend_mempcpy(p, Z_STRVAL_P(entry_value), value_len);
617 }
618 } ZEND_HASH_FOREACH_END();
619 }
620 } else {
621 /* cannot put the data - skip */
622 }
623 }
624 }
625 if (packet->is_change_user_packet) {
626 enum_func_status ret = FAIL;
627 const MYSQLND_CSTRING payload = {(char*) buffer + MYSQLND_HEADER_SIZE, p - (buffer + MYSQLND_HEADER_SIZE)};
628 const unsigned int silent = packet->silent;
629
630 ret = conn->command->change_user(conn, payload, silent);
631 DBG_RETURN(ret == PASS? (p - buffer - MYSQLND_HEADER_SIZE) : 0);
632 } else {
633 /*
634 The auth handshake packet has no command in it. Thus we can't go over conn->command directly.
635 Well, we can have a command->no_command(conn, payload)
636 */
637 const size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
638 if (!sent) {
639 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
640 }
641 DBG_RETURN(sent);
642 }
643 }
644 /* }}} */
645
646
647 /* {{{ php_mysqlnd_auth_response_read */
648 static enum_func_status
php_mysqlnd_auth_response_read(MYSQLND_CONN_DATA * conn,void * _packet)649 php_mysqlnd_auth_response_read(MYSQLND_CONN_DATA * conn, void * _packet)
650 {
651 MYSQLND_PACKET_AUTH_RESPONSE * packet= (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
652 MYSQLND_ERROR_INFO * error_info = conn->error_info;
653 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
654 MYSQLND_VIO * vio = conn->vio;
655 MYSQLND_STATS * stats = conn->stats;
656 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
657 const size_t buf_len = pfc->cmd_buffer.length;
658 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
659 const zend_uchar * p = buf;
660 const zend_uchar * const begin = buf;
661
662 DBG_ENTER("php_mysqlnd_auth_response_read");
663
664 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "OK", PROT_OK_PACKET)) {
665 DBG_RETURN(FAIL);
666 }
667 BAIL_IF_NO_MORE_DATA;
668
669 /* Should be always 0x0 or ERROR_MARKER for error */
670 packet->response_code = uint1korr(p);
671 p++;
672 BAIL_IF_NO_MORE_DATA;
673
674 if (ERROR_MARKER == packet->response_code) {
675 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
676 packet->error, sizeof(packet->error),
677 &packet->error_no, packet->sqlstate
678 );
679 DBG_RETURN(PASS);
680 }
681 if (0xFE == packet->response_code) {
682 /* Authentication Switch Response */
683 if (packet->header.size > (size_t) (p - buf)) {
684 packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
685 packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
686 p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
687
688 packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
689 if (packet->new_auth_protocol_data_len) {
690 packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
691 memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
692 }
693 DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
694 DBG_INF_FMT("Server salt : [%zu][%.*s]", packet->new_auth_protocol_data_len, (int) packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
695 }
696 } else {
697 zend_ulong net_len;
698 /* Everything was fine! */
699 packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
700 BAIL_IF_NO_MORE_DATA;
701
702 packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
703 BAIL_IF_NO_MORE_DATA;
704
705 packet->server_status = uint2korr(p);
706 p+= 2;
707 BAIL_IF_NO_MORE_DATA;
708
709 packet->warning_count = uint2korr(p);
710 p+= 2;
711 BAIL_IF_NO_MORE_DATA;
712
713 /* There is a message */
714 if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
715 packet->message_len = MIN(net_len, buf_len - (p - begin));
716 packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
717 } else {
718 packet->message = NULL;
719 packet->message_len = 0;
720 }
721
722 DBG_INF_FMT("OK packet: aff_rows=%" PRIu64 " last_ins_id=%" PRIu64 " server_status=%u warnings=%u",
723 packet->affected_rows, packet->last_insert_id, packet->server_status,
724 packet->warning_count);
725 }
726
727 DBG_RETURN(PASS);
728 premature_end:
729 DBG_ERR_FMT("OK packet %zu bytes shorter than expected", p - begin - packet->header.size);
730 php_error_docref(NULL, E_WARNING, "AUTH_RESPONSE packet %zu bytes shorter than expected",
731 p - begin - packet->header.size);
732 DBG_RETURN(FAIL);
733 }
734 /* }}} */
735
736
737 /* {{{ php_mysqlnd_auth_response_free_mem */
738 static void
php_mysqlnd_auth_response_free_mem(void * _packet)739 php_mysqlnd_auth_response_free_mem(void * _packet)
740 {
741 MYSQLND_PACKET_AUTH_RESPONSE * p = (MYSQLND_PACKET_AUTH_RESPONSE *) _packet;
742 if (p->message) {
743 mnd_efree(p->message);
744 p->message = NULL;
745 }
746 if (p->new_auth_protocol) {
747 mnd_efree(p->new_auth_protocol);
748 p->new_auth_protocol = NULL;
749 }
750 p->new_auth_protocol_len = 0;
751
752 if (p->new_auth_protocol_data) {
753 mnd_efree(p->new_auth_protocol_data);
754 p->new_auth_protocol_data = NULL;
755 }
756 p->new_auth_protocol_data_len = 0;
757 }
758 /* }}} */
759
760
761 /* {{{ php_mysqlnd_change_auth_response_write */
762 static size_t
php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn,void * _packet)763 php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn, void * _packet)
764 {
765 MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *packet= (MYSQLND_PACKET_CHANGE_AUTH_RESPONSE *) _packet;
766 MYSQLND_ERROR_INFO * error_info = conn->error_info;
767 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
768 MYSQLND_VIO * vio = conn->vio;
769 MYSQLND_STATS * stats = conn->stats;
770 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
771 size_t total_packet_size = packet->auth_data_len + MYSQLND_HEADER_SIZE;
772 zend_uchar * const buffer = pfc->cmd_buffer.length >= total_packet_size? pfc->cmd_buffer.buffer : mnd_emalloc(total_packet_size);
773 zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
774
775 DBG_ENTER("php_mysqlnd_change_auth_response_write");
776
777 if (packet->auth_data_len) {
778 p = zend_mempcpy(p, packet->auth_data, packet->auth_data_len);
779 }
780
781 {
782 /*
783 The auth handshake packet has no command in it. Thus we can't go over conn->command directly.
784 Well, we can have a command->no_command(conn, payload)
785 */
786 const size_t sent = pfc->data->m.send(pfc, vio, buffer, p - buffer - MYSQLND_HEADER_SIZE, stats, error_info);
787 if (buffer != pfc->cmd_buffer.buffer) {
788 mnd_efree(buffer);
789 }
790 if (!sent) {
791 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
792 }
793 DBG_RETURN(sent);
794 }
795 }
796 /* }}} */
797
798
799 /* {{{ php_mysqlnd_ok_read */
800 static enum_func_status
php_mysqlnd_ok_read(MYSQLND_CONN_DATA * conn,void * _packet)801 php_mysqlnd_ok_read(MYSQLND_CONN_DATA * conn, void * _packet)
802 {
803 MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
804 MYSQLND_ERROR_INFO * error_info = conn->error_info;
805 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
806 MYSQLND_VIO * vio = conn->vio;
807 MYSQLND_STATS * stats = conn->stats;
808 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
809 const size_t buf_len = pfc->cmd_buffer.length;
810 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
811 const zend_uchar * p = buf;
812 const zend_uchar * const begin = buf;
813 zend_ulong net_len;
814
815 DBG_ENTER("php_mysqlnd_ok_read");
816
817 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "OK", PROT_OK_PACKET)) {
818 DBG_RETURN(FAIL);
819 }
820 BAIL_IF_NO_MORE_DATA;
821
822 /* Should be always 0x0 or ERROR_MARKER for error */
823 packet->field_count = uint1korr(p);
824 p++;
825 BAIL_IF_NO_MORE_DATA;
826
827 if (ERROR_MARKER == packet->field_count) {
828 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
829 packet->error, sizeof(packet->error),
830 &packet->error_no, packet->sqlstate
831 );
832 DBG_RETURN(PASS);
833 }
834 /* Everything was fine! */
835 packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
836 BAIL_IF_NO_MORE_DATA;
837
838 packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
839 BAIL_IF_NO_MORE_DATA;
840
841 packet->server_status = uint2korr(p);
842 p+= 2;
843 BAIL_IF_NO_MORE_DATA;
844
845 packet->warning_count = uint2korr(p);
846 p+= 2;
847 BAIL_IF_NO_MORE_DATA;
848
849 /* There is a message */
850 if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
851 packet->message_len = MIN(net_len, buf_len - (p - begin));
852 packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
853 } else {
854 packet->message = NULL;
855 packet->message_len = 0;
856 }
857
858 DBG_INF_FMT("OK packet: aff_rows=%" PRIu64 " last_ins_id=%" PRIu64 " server_status=%u warnings=%u",
859 packet->affected_rows, packet->last_insert_id, packet->server_status,
860 packet->warning_count);
861
862 BAIL_IF_NO_MORE_DATA;
863
864 DBG_RETURN(PASS);
865 premature_end:
866 DBG_ERR_FMT("OK packet %zu bytes shorter than expected", p - begin - packet->header.size);
867 php_error_docref(NULL, E_WARNING, "OK packet %zu bytes shorter than expected",
868 p - begin - packet->header.size);
869 DBG_RETURN(FAIL);
870 }
871 /* }}} */
872
873
874 /* {{{ php_mysqlnd_ok_free_mem */
875 static void
php_mysqlnd_ok_free_mem(void * _packet)876 php_mysqlnd_ok_free_mem(void * _packet)
877 {
878 MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet;
879 if (p->message) {
880 mnd_efree(p->message);
881 p->message = NULL;
882 }
883 }
884 /* }}} */
885
886
887 /* {{{ php_mysqlnd_eof_read */
888 static enum_func_status
php_mysqlnd_eof_read(MYSQLND_CONN_DATA * conn,void * _packet)889 php_mysqlnd_eof_read(MYSQLND_CONN_DATA * conn, void * _packet)
890 {
891 /*
892 EOF packet is since 4.1 five bytes long,
893 but we can get also an error, make it bigger.
894
895 Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
896 */
897 MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
898 MYSQLND_ERROR_INFO * error_info = conn->error_info;
899 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
900 MYSQLND_VIO * vio = conn->vio;
901 MYSQLND_STATS * stats = conn->stats;
902 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
903 const size_t buf_len = pfc->cmd_buffer.length;
904 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
905 const zend_uchar * p = buf;
906 const zend_uchar * const begin = buf;
907
908 DBG_ENTER("php_mysqlnd_eof_read");
909
910 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "EOF", PROT_EOF_PACKET)) {
911 DBG_RETURN(FAIL);
912 }
913 BAIL_IF_NO_MORE_DATA;
914
915 /* Should be always EODATA_MARKER */
916 packet->field_count = uint1korr(p);
917 p++;
918 BAIL_IF_NO_MORE_DATA;
919
920 if (ERROR_MARKER == packet->field_count) {
921 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
922 packet->error, sizeof(packet->error),
923 &packet->error_no, packet->sqlstate
924 );
925 DBG_RETURN(PASS);
926 }
927
928 /*
929 4.1 sends 1 byte EOF packet after metadata of
930 PREPARE/EXECUTE but 5 bytes after the result. This is not
931 according to the Docs@Forge!!!
932 */
933 if (packet->header.size > 1) {
934 packet->warning_count = uint2korr(p);
935 p+= 2;
936 BAIL_IF_NO_MORE_DATA;
937
938 packet->server_status = uint2korr(p);
939 p+= 2;
940 BAIL_IF_NO_MORE_DATA;
941 } else {
942 packet->warning_count = 0;
943 packet->server_status = 0;
944 }
945
946 BAIL_IF_NO_MORE_DATA;
947
948 DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u",
949 packet->field_count, packet->server_status, packet->warning_count);
950
951 DBG_RETURN(PASS);
952 premature_end:
953 DBG_ERR_FMT("EOF packet %zu bytes shorter than expected", p - begin - packet->header.size);
954 php_error_docref(NULL, E_WARNING, "EOF packet %zu bytes shorter than expected",
955 p - begin - packet->header.size);
956 DBG_RETURN(FAIL);
957 }
958 /* }}} */
959
960
961 /* {{{ php_mysqlnd_cmd_write */
php_mysqlnd_cmd_write(MYSQLND_CONN_DATA * conn,void * _packet)962 size_t php_mysqlnd_cmd_write(MYSQLND_CONN_DATA * conn, void * _packet)
963 {
964 /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
965 MYSQLND_PACKET_COMMAND * packet= (MYSQLND_PACKET_COMMAND *) _packet;
966 MYSQLND_ERROR_INFO * error_info = conn->error_info;
967 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
968 MYSQLND_VIO * vio = conn->vio;
969 MYSQLND_STATS * stats = conn->stats;
970 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
971 size_t sent = 0;
972
973 DBG_ENTER("php_mysqlnd_cmd_write");
974 /*
975 Reset packet_no, or we will get bad handshake!
976 Every command starts a new TX and packet numbers are reset to 0.
977 */
978 pfc->data->m.reset(pfc, stats, error_info);
979
980 MYSQLND_INC_CONN_STATISTIC(stats, STAT_PACKETS_SENT_CMD);
981
982 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
983 vio->data->m.consume_uneaten_data(vio, packet->command);
984 #endif
985
986 if (!packet->argument.s || !packet->argument.l) {
987 zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
988
989 int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
990 sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
991 } else {
992 size_t tmp_len = packet->argument.l + 1 + MYSQLND_HEADER_SIZE;
993 zend_uchar *tmp, *p;
994 tmp = (tmp_len > pfc->cmd_buffer.length)? mnd_emalloc(tmp_len):pfc->cmd_buffer.buffer;
995 if (!tmp) {
996 goto end;
997 }
998 p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
999
1000 int1store(p, packet->command);
1001 p++;
1002
1003 memcpy(p, packet->argument.s, packet->argument.l);
1004
1005 sent = pfc->data->m.send(pfc, vio, tmp, tmp_len - MYSQLND_HEADER_SIZE, stats, error_info);
1006 if (tmp != pfc->cmd_buffer.buffer) {
1007 MYSQLND_INC_CONN_STATISTIC(stats, STAT_CMD_BUFFER_TOO_SMALL);
1008 mnd_efree(tmp);
1009 }
1010 }
1011 end:
1012 if (!sent) {
1013 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
1014 }
1015 DBG_RETURN(sent);
1016 }
1017 /* }}} */
1018
1019
1020 /* {{{ php_mysqlnd_rset_header_read */
1021 static enum_func_status
php_mysqlnd_rset_header_read(MYSQLND_CONN_DATA * conn,void * _packet)1022 php_mysqlnd_rset_header_read(MYSQLND_CONN_DATA * conn, void * _packet)
1023 {
1024 MYSQLND_PACKET_RSET_HEADER * packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
1025 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1026 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1027 MYSQLND_VIO * vio = conn->vio;
1028 MYSQLND_STATS * stats = conn->stats;
1029 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
1030 enum_func_status ret = PASS;
1031 const size_t buf_len = pfc->cmd_buffer.length;
1032 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
1033 const zend_uchar * p = buf;
1034 const zend_uchar * const begin = buf;
1035 size_t len;
1036
1037 DBG_ENTER("php_mysqlnd_rset_header_read");
1038
1039 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET)) {
1040 DBG_RETURN(FAIL);
1041 }
1042 BAIL_IF_NO_MORE_DATA;
1043
1044 /*
1045 Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
1046 of encoded sequence for length.
1047 */
1048 if (ERROR_MARKER == *p) {
1049 /* Error */
1050 p++;
1051 BAIL_IF_NO_MORE_DATA;
1052 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1053 packet->error_info.error, sizeof(packet->error_info.error),
1054 &packet->error_info.error_no, packet->error_info.sqlstate
1055 );
1056 DBG_RETURN(PASS);
1057 }
1058
1059 packet->field_count = php_mysqlnd_net_field_length(&p);
1060 BAIL_IF_NO_MORE_DATA;
1061
1062 switch (packet->field_count) {
1063 case MYSQLND_NULL_LENGTH:
1064 DBG_INF("LOAD LOCAL");
1065 /*
1066 First byte in the packet is the field count.
1067 Thus, the name is size - 1. And we add 1 for a trailing \0.
1068 Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed
1069 that packet->header.size is > 0. Which means that len can't underflow, that
1070 would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
1071 */
1072 len = packet->header.size - 1;
1073 packet->info_or_local_file.s = mnd_emalloc(len + 1);
1074 memcpy(packet->info_or_local_file.s, p, len);
1075 packet->info_or_local_file.s[len] = '\0';
1076 packet->info_or_local_file.l = len;
1077 break;
1078 case 0x00:
1079 DBG_INF("UPSERT");
1080 packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
1081 BAIL_IF_NO_MORE_DATA;
1082
1083 packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
1084 BAIL_IF_NO_MORE_DATA;
1085
1086 packet->server_status = uint2korr(p);
1087 p+=2;
1088 BAIL_IF_NO_MORE_DATA;
1089
1090 packet->warning_count = uint2korr(p);
1091 p+=2;
1092 BAIL_IF_NO_MORE_DATA;
1093 /* Check for additional textual data */
1094 if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
1095 packet->info_or_local_file.s = mnd_emalloc(len + 1);
1096 memcpy(packet->info_or_local_file.s, p, len);
1097 packet->info_or_local_file.s[len] = '\0';
1098 packet->info_or_local_file.l = len;
1099 }
1100 DBG_INF_FMT("affected_rows=%" PRIu64 " last_insert_id=%" PRIu64 " server_status=%u warning_count=%u",
1101 packet->affected_rows, packet->last_insert_id,
1102 packet->server_status, packet->warning_count);
1103 break;
1104 default:
1105 DBG_INF("SELECT");
1106 /* Result set */
1107 break;
1108 }
1109 BAIL_IF_NO_MORE_DATA;
1110
1111 DBG_RETURN(ret);
1112 premature_end:
1113 DBG_ERR_FMT("RSET_HEADER packet %zu bytes shorter than expected", p - begin - packet->header.size);
1114 php_error_docref(NULL, E_WARNING, "RSET_HEADER packet %zu bytes shorter than expected",
1115 p - begin - packet->header.size);
1116 DBG_RETURN(FAIL);
1117 }
1118 /* }}} */
1119
1120
1121 /* {{{ php_mysqlnd_rset_header_free_mem */
1122 static
php_mysqlnd_rset_header_free_mem(void * _packet)1123 void php_mysqlnd_rset_header_free_mem(void * _packet)
1124 {
1125 MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
1126 DBG_ENTER("php_mysqlnd_rset_header_free_mem");
1127 mysqlnd_set_string(&p->info_or_local_file, NULL, 0);
1128 DBG_VOID_RETURN;
1129 }
1130 /* }}} */
1131
1132 #define READ_RSET_FIELD(field_name) do { \
1133 len = php_mysqlnd_net_field_length(&p); \
1134 if (UNEXPECTED(len == MYSQLND_NULL_LENGTH)) { \
1135 goto faulty_or_fake; \
1136 } else if (len != 0) { \
1137 meta->field_name = (const char *)p; \
1138 meta->field_name ## _length = len; \
1139 p += len; \
1140 total_len += len + 1; \
1141 } else { \
1142 meta->field_name = mysqlnd_empty_string; \
1143 meta->field_name ## _length = 0; \
1144 } \
1145 } while (0)
1146
1147
1148 /* {{{ php_mysqlnd_rset_field_read */
1149 static enum_func_status
php_mysqlnd_rset_field_read(MYSQLND_CONN_DATA * conn,void * _packet)1150 php_mysqlnd_rset_field_read(MYSQLND_CONN_DATA * conn, void * _packet)
1151 {
1152 /* Should be enough for the metadata of a single row */
1153 MYSQLND_PACKET_RES_FIELD *packet = (MYSQLND_PACKET_RES_FIELD *) _packet;
1154 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1155 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1156 MYSQLND_VIO * vio = conn->vio;
1157 MYSQLND_STATS * stats = conn->stats;
1158 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
1159 const size_t buf_len = pfc->cmd_buffer.length;
1160 size_t total_len = 0;
1161 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
1162 const zend_uchar * p = buf;
1163 const zend_uchar * const begin = buf;
1164 char *root_ptr;
1165 zend_ulong len;
1166 MYSQLND_FIELD *meta;
1167
1168 DBG_ENTER("php_mysqlnd_rset_field_read");
1169
1170 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "field", PROT_RSET_FLD_PACKET)) {
1171 DBG_RETURN(FAIL);
1172 }
1173
1174 if (packet->skip_parsing) {
1175 DBG_RETURN(PASS);
1176 }
1177
1178 BAIL_IF_NO_MORE_DATA;
1179 if (ERROR_MARKER == *p) {
1180 /* Error */
1181 p++;
1182 BAIL_IF_NO_MORE_DATA;
1183 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1184 packet->error_info.error, sizeof(packet->error_info.error),
1185 &packet->error_info.error_no, packet->error_info.sqlstate
1186 );
1187 DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
1188 DBG_RETURN(PASS);
1189 } else if (EODATA_MARKER == *p && packet->header.size < 8) {
1190 /* Premature EOF. That should be COM_FIELD_LIST. But we don't support COM_FIELD_LIST anymore, thus this should not happen */
1191 DBG_ERR("Premature EOF. That should be COM_FIELD_LIST");
1192 php_error_docref(NULL, E_WARNING, "Premature EOF in result field metadata");
1193 DBG_RETURN(FAIL);
1194 }
1195
1196 meta = packet->metadata;
1197
1198 READ_RSET_FIELD(catalog);
1199 READ_RSET_FIELD(db);
1200 READ_RSET_FIELD(table);
1201 READ_RSET_FIELD(org_table);
1202 READ_RSET_FIELD(name);
1203 READ_RSET_FIELD(org_name);
1204
1205 /* 1 byte length */
1206 if (UNEXPECTED(12 != *p)) {
1207 DBG_ERR_FMT("Protocol error. Server sent false length. Expected 12 got %d", (int) *p);
1208 php_error_docref(NULL, E_WARNING, "Protocol error. Server sent false length. Expected 12");
1209 }
1210
1211 if ((size_t)((p - begin) + 12) > packet->header.size) {
1212 php_error_docref(NULL, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__);
1213 goto premature_end;
1214 }
1215
1216 p++;
1217
1218 meta->charsetnr = uint2korr(p);
1219 p += 2;
1220
1221 meta->length = uint4korr(p);
1222 p += 4;
1223
1224 meta->type = uint1korr(p);
1225 p += 1;
1226
1227 meta->flags = uint2korr(p);
1228 p += 2;
1229
1230 meta->decimals = uint1korr(p);
1231 p += 1;
1232
1233 /* 2 byte filler */
1234 p +=2;
1235
1236 /* Should we set NUM_FLAG (libmysql does it) ? */
1237 if (
1238 (meta->type <= MYSQL_TYPE_INT24 &&
1239 (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
1240 ) || meta->type == MYSQL_TYPE_YEAR)
1241 {
1242 meta->flags |= NUM_FLAG;
1243 }
1244
1245
1246 /*
1247 def could be empty, thus don't allocate on the root.
1248 NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
1249 Otherwise the string is length encoded.
1250 */
1251 if (packet->header.size > (size_t) (p - buf) &&
1252 (len = php_mysqlnd_net_field_length(&p)) &&
1253 len != MYSQLND_NULL_LENGTH)
1254 {
1255 BAIL_IF_NO_MORE_DATA;
1256 DBG_INF_FMT("Def found, length " ZEND_ULONG_FMT, len);
1257 meta->def = packet->memory_pool->get_chunk(packet->memory_pool, len + 1);
1258 memcpy(meta->def, p, len);
1259 meta->def[len] = '\0';
1260 meta->def_length = len;
1261 p += len;
1262 }
1263
1264 root_ptr = meta->root = packet->memory_pool->get_chunk(packet->memory_pool, total_len);
1265 meta->root_len = total_len;
1266
1267 if (EXPECTED(meta->name_length != 0)) {
1268 meta->sname = zend_string_init_interned(meta->name, meta->name_length, 0);
1269 meta->name = ZSTR_VAL(meta->sname);
1270 } else {
1271 meta->sname = ZSTR_EMPTY_ALLOC();
1272 }
1273
1274 /* Now do allocs */
1275 if (meta->catalog_length != 0) {
1276 len = meta->catalog_length;
1277 meta->catalog = memcpy(root_ptr, meta->catalog, len);
1278 *(root_ptr +=len) = '\0';
1279 root_ptr++;
1280 }
1281
1282 if (meta->db_length != 0) {
1283 len = meta->db_length;
1284 meta->db = memcpy(root_ptr, meta->db, len);
1285 *(root_ptr +=len) = '\0';
1286 root_ptr++;
1287 }
1288
1289 if (meta->table_length != 0) {
1290 len = meta->table_length;
1291 meta->table = memcpy(root_ptr, meta->table, len);
1292 *(root_ptr +=len) = '\0';
1293 root_ptr++;
1294 }
1295
1296 if (meta->org_table_length != 0) {
1297 len = meta->org_table_length;
1298 meta->org_table = memcpy(root_ptr, meta->org_table, len);
1299 *(root_ptr +=len) = '\0';
1300 root_ptr++;
1301 }
1302
1303 if (meta->org_name_length != 0) {
1304 len = meta->org_name_length;
1305 meta->org_name = memcpy(root_ptr, meta->org_name, len);
1306 *(root_ptr +=len) = '\0';
1307 root_ptr++;
1308 }
1309
1310 DBG_INF_FMT("allocing root.");
1311
1312 DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
1313 meta->name? meta->name:"*NA*");
1314
1315 DBG_RETURN(PASS);
1316
1317 faulty_or_fake:
1318 DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
1319 php_error_docref(NULL, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
1320 " The server is faulty");
1321 DBG_RETURN(FAIL);
1322 premature_end:
1323 DBG_ERR_FMT("RSET field packet %zu bytes shorter than expected", p - begin - packet->header.size);
1324 php_error_docref(NULL, E_WARNING, "Result set field packet %zu bytes "
1325 "shorter than expected", p - begin - packet->header.size);
1326 DBG_RETURN(FAIL);
1327 }
1328 /* }}} */
1329
1330 /* Like SET_CLIENT_ERROR, but for packet error_info. The type is the same,
1331 * but only some parts of it are used. */
set_packet_error(MYSQLND_ERROR_INFO * info,unsigned err_no,const char * sqlstate,const char * error)1332 static void set_packet_error(
1333 MYSQLND_ERROR_INFO *info, unsigned err_no, const char *sqlstate, const char *error)
1334 {
1335 info->error_no = err_no;
1336 strlcpy(info->sqlstate, sqlstate, sizeof(info->sqlstate));
1337 strlcpy(info->error, error, sizeof(info->error));
1338 }
1339
1340 /* {{{ php_mysqlnd_read_row_ex */
1341 static enum_func_status
php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,MYSQLND_VIO * vio,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info,MYSQLND_CONNECTION_STATE * connection_state,MYSQLND_MEMORY_POOL * pool,MYSQLND_ROW_BUFFER * buffer,size_t * const data_size)1342 php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
1343 MYSQLND_VIO * vio,
1344 MYSQLND_STATS * stats,
1345 MYSQLND_ERROR_INFO * error_info,
1346 MYSQLND_CONNECTION_STATE * connection_state,
1347 MYSQLND_MEMORY_POOL * pool,
1348 MYSQLND_ROW_BUFFER * buffer,
1349 size_t * const data_size)
1350 {
1351 enum_func_status ret = PASS;
1352 MYSQLND_PACKET_HEADER header;
1353 zend_uchar * p = NULL;
1354 size_t prealloc_more_bytes;
1355
1356 DBG_ENTER("php_mysqlnd_read_row_ex");
1357
1358 /*
1359 To ease the process the server splits everything in packets up to 2^24 - 1.
1360 Even in the case the payload is evenly divisible by this value, the last
1361 packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
1362 for next one if they have 2^24 - 1 sizes. But just read the header of a
1363 zero-length byte, don't read the body, there is no such.
1364 */
1365
1366 /*
1367 We're allocating an extra byte, as php_mysqlnd_rowp_read_text_protocol
1368 needs to be able to append a terminating \0 for atoi/atof.
1369 */
1370 prealloc_more_bytes = 1;
1371
1372 *data_size = 0;
1373 if (UNEXPECTED(FAIL == mysqlnd_read_header(pfc, vio, &header, stats, error_info))) {
1374 ret = FAIL;
1375 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
1376 set_packet_error(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
1377 } else {
1378 /* If the packet is split in multiple chunks, allocate a temporary buffer that we can
1379 * reallocate, and only afterwards copy it to the pool when we know the final size. */
1380 zend_uchar *buf = NULL;
1381 while (header.size >= MYSQLND_MAX_PACKET_SIZE) {
1382 buf = erealloc(buf, *data_size + header.size);
1383 p = buf + *data_size;
1384 *data_size += header.size;
1385
1386 if (UNEXPECTED(PASS != (ret = pfc->data->m.receive(pfc, vio, p, header.size, stats, error_info)))) {
1387 DBG_ERR("Empty row packet body");
1388 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
1389 set_packet_error(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
1390 efree(buf);
1391 DBG_RETURN(FAIL);
1392 }
1393 if (FAIL == mysqlnd_read_header(pfc, vio, &header, stats, error_info)) {
1394 efree(buf);
1395 DBG_RETURN(FAIL);
1396 }
1397 }
1398
1399 buffer->ptr = pool->get_chunk(pool, *data_size + header.size + prealloc_more_bytes);
1400 if (buf) {
1401 memcpy(buffer->ptr, buf, *data_size);
1402 efree(buf);
1403 }
1404 p = (zend_uchar *) buffer->ptr + *data_size;
1405 *data_size += header.size;
1406
1407 if (UNEXPECTED(PASS != (ret = pfc->data->m.receive(pfc, vio, p, header.size, stats, error_info)))) {
1408 DBG_ERR("Empty row packet body");
1409 SET_CONNECTION_STATE(connection_state, CONN_QUIT_SENT);
1410 set_packet_error(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
1411 }
1412 }
1413 DBG_RETURN(ret);
1414 }
1415 /* }}} */
1416
1417
1418 /* {{{ php_mysqlnd_rowp_read_binary_protocol */
1419 enum_func_status
php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer,zval * fields,const unsigned int field_count,const MYSQLND_FIELD * const fields_metadata,const bool as_int_or_float,MYSQLND_STATS * const stats)1420 php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
1421 const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata,
1422 const bool as_int_or_float, MYSQLND_STATS * const stats)
1423 {
1424 unsigned int i;
1425 const zend_uchar * p = row_buffer->ptr;
1426 const zend_uchar * null_ptr;
1427 zend_uchar bit;
1428 zval *current_field, *end_field, *start_field;
1429
1430 DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
1431
1432 if (!fields) {
1433 DBG_RETURN(FAIL);
1434 }
1435
1436 end_field = (start_field = fields) + field_count;
1437
1438 /* skip the first byte, not EODATA_MARKER -> 0x0, status */
1439 p++;
1440 null_ptr= p;
1441 p += (field_count + 9)/8; /* skip null bits */
1442 bit = 4; /* first 2 bits are reserved */
1443
1444 for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1445 enum_mysqlnd_collected_stats statistic;
1446 const zend_uchar * orig_p = p;
1447
1448 DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u",
1449 current_field, i,
1450 fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
1451 fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT);
1452 if (*null_ptr & bit) {
1453 DBG_INF("It's null");
1454 ZVAL_NULL(current_field);
1455 statistic = STAT_BINARY_TYPE_FETCHED_NULL;
1456 } else {
1457 enum_mysqlnd_field_types type = fields_metadata[i].type;
1458 mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], 0, &p);
1459
1460 if (MYSQLND_G(collect_statistics)) {
1461 switch (fields_metadata[i].type) {
1462 case MYSQL_TYPE_DECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
1463 case MYSQL_TYPE_TINY: statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
1464 case MYSQL_TYPE_SHORT: statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
1465 case MYSQL_TYPE_LONG: statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
1466 case MYSQL_TYPE_FLOAT: statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
1467 case MYSQL_TYPE_DOUBLE: statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
1468 case MYSQL_TYPE_NULL: statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
1469 case MYSQL_TYPE_TIMESTAMP: statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
1470 case MYSQL_TYPE_LONGLONG: statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
1471 case MYSQL_TYPE_INT24: statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
1472 case MYSQL_TYPE_DATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
1473 case MYSQL_TYPE_TIME: statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
1474 case MYSQL_TYPE_DATETIME: statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
1475 case MYSQL_TYPE_YEAR: statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
1476 case MYSQL_TYPE_NEWDATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
1477 case MYSQL_TYPE_VARCHAR: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1478 case MYSQL_TYPE_BIT: statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
1479 case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
1480 case MYSQL_TYPE_ENUM: statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
1481 case MYSQL_TYPE_SET: statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
1482 case MYSQL_TYPE_TINY_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1483 case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1484 case MYSQL_TYPE_LONG_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1485 case MYSQL_TYPE_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1486 case MYSQL_TYPE_VECTOR: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1487 case MYSQL_TYPE_VAR_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1488 case MYSQL_TYPE_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1489 case MYSQL_TYPE_GEOMETRY: statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
1490 default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
1491 }
1492 }
1493 }
1494 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
1495 STAT_BYTES_RECEIVED_PURE_DATA_PS,
1496 (Z_TYPE_P(current_field) == IS_STRING)?
1497 Z_STRLEN_P(current_field) : (size_t)(p - orig_p));
1498
1499 if (!((bit<<=1) & 255)) {
1500 bit = 1; /* to the following byte */
1501 null_ptr++;
1502 }
1503 }
1504
1505 DBG_RETURN(PASS);
1506 }
1507 /* }}} */
1508
1509
1510 /* {{{ php_mysqlnd_rowp_read_text_protocol */
1511 enum_func_status
php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER * row_buffer,zval * fields,unsigned int field_count,const MYSQLND_FIELD * fields_metadata,bool as_int_or_float,MYSQLND_STATS * stats)1512 php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
1513 unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
1514 bool as_int_or_float, MYSQLND_STATS * stats)
1515 {
1516 unsigned int i;
1517 zval *current_field, *end_field, *start_field;
1518 zend_uchar * p = row_buffer->ptr;
1519 const size_t data_size = row_buffer->size;
1520 const zend_uchar * const packet_end = (zend_uchar*) p + data_size;
1521
1522 DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
1523
1524 if (!fields) {
1525 DBG_RETURN(FAIL);
1526 }
1527
1528 end_field = (start_field = fields) + field_count;
1529
1530 for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1531 /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
1532 const zend_ulong len = php_mysqlnd_net_field_length((const zend_uchar **) &p);
1533
1534 /* NULL or NOT NULL, this is the question! */
1535 if (len == MYSQLND_NULL_LENGTH) {
1536 ZVAL_NULL(current_field);
1537 } else if ((p + len) > packet_end) {
1538 php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing %zu"
1539 " bytes after end of packet", (p + len) - packet_end - 1);
1540 DBG_RETURN(FAIL);
1541 } else {
1542 struct st_mysqlnd_perm_bind perm_bind =
1543 mysqlnd_ps_fetch_functions[fields_metadata[i].type];
1544 if (MYSQLND_G(collect_statistics)) {
1545 enum_mysqlnd_collected_stats statistic;
1546 switch (fields_metadata[i].type) {
1547 case MYSQL_TYPE_DECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1548 case MYSQL_TYPE_TINY: statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
1549 case MYSQL_TYPE_SHORT: statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
1550 case MYSQL_TYPE_LONG: statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
1551 case MYSQL_TYPE_FLOAT: statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
1552 case MYSQL_TYPE_DOUBLE: statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
1553 case MYSQL_TYPE_NULL: statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
1554 case MYSQL_TYPE_TIMESTAMP: statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
1555 case MYSQL_TYPE_LONGLONG: statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
1556 case MYSQL_TYPE_INT24: statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
1557 case MYSQL_TYPE_DATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1558 case MYSQL_TYPE_TIME: statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
1559 case MYSQL_TYPE_DATETIME: statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
1560 case MYSQL_TYPE_YEAR: statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
1561 case MYSQL_TYPE_NEWDATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1562 case MYSQL_TYPE_VARCHAR: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1563 case MYSQL_TYPE_BIT: statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
1564 case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1565 case MYSQL_TYPE_ENUM: statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
1566 case MYSQL_TYPE_SET: statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
1567 case MYSQL_TYPE_JSON: statistic = STAT_TEXT_TYPE_FETCHED_JSON; break;
1568 case MYSQL_TYPE_TINY_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1569 case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1570 case MYSQL_TYPE_LONG_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1571 case MYSQL_TYPE_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1572 case MYSQL_TYPE_VECTOR: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1573 case MYSQL_TYPE_VAR_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1574 case MYSQL_TYPE_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1575 case MYSQL_TYPE_GEOMETRY: statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
1576 default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
1577 }
1578 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
1579 }
1580 if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
1581 /*
1582 BIT fields are specially handled. As they come as bit mask, they have
1583 to be converted to human-readable representation.
1584 */
1585 ps_fetch_from_1_to_8_bytes(current_field, &(fields_metadata[i]), 0, (const zend_uchar **) &p, len);
1586 /*
1587 We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
1588 later in this function there will be an advancement.
1589 */
1590 p -= len;
1591 if (Z_TYPE_P(current_field) == IS_LONG && !as_int_or_float) {
1592 /* we are using the text protocol, so convert to string */
1593 char tmp[22];
1594 const size_t tmp_len = snprintf(tmp, sizeof(tmp), ZEND_ULONG_FMT, Z_LVAL_P(current_field));
1595 ZVAL_STRINGL(current_field, tmp, tmp_len);
1596 } else if (Z_TYPE_P(current_field) == IS_STRING) {
1597 /* nothing to do here, as we want a string and ps_fetch_from_1_to_8_bytes() has given us one */
1598 }
1599 } else if (as_int_or_float && perm_bind.php_type == IS_LONG
1600 && !(fields_metadata[i].flags & ZEROFILL_FLAG)) {
1601 zend_uchar save = *(p + len);
1602 /* We have to make it ASCIIZ temporarily */
1603 *(p + len) = '\0';
1604 if (perm_bind.pack_len < SIZEOF_ZEND_LONG) {
1605 /* direct conversion */
1606 int64_t v =
1607 #ifndef PHP_WIN32
1608 atoll((char *) p);
1609 #else
1610 _atoi64((char *) p);
1611 #endif
1612 ZVAL_LONG(current_field, (zend_long) v); /* the cast is safe */
1613 } else {
1614 uint64_t v =
1615 #ifndef PHP_WIN32
1616 strtoull((char *) p, NULL, 10);
1617 #else
1618 _strtoui64((char *) p, NULL, 10);
1619 #endif
1620 bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
1621 /* We have to make it ASCIIZ temporarily */
1622 #if SIZEOF_ZEND_LONG==8
1623 if (uns == TRUE && v > 9223372036854775807L)
1624 #elif SIZEOF_ZEND_LONG==4
1625 if ((uns == TRUE && v > L64(2147483647)) ||
1626 (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
1627 (L64(-2147483648) > (int64_t) v))))
1628 #else
1629 #error Need fix for this architecture
1630 #endif /* SIZEOF */
1631 {
1632 ZVAL_STRINGL(current_field, (char *)p, len);
1633 } else {
1634 ZVAL_LONG(current_field, (zend_long) v); /* the cast is safe */
1635 }
1636 }
1637 *(p + len) = save;
1638 } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
1639 zend_uchar save = *(p + len);
1640 /* We have to make it ASCIIZ temporarily */
1641 *(p + len) = '\0';
1642 ZVAL_DOUBLE(current_field, zend_strtod((char *) p, NULL));
1643 *(p + len) = save;
1644 } else {
1645 ZVAL_STRINGL_FAST(current_field, (char *)p, len);
1646 }
1647 p += len;
1648 }
1649 }
1650
1651 DBG_RETURN(PASS);
1652 }
1653 /* }}} */
1654
1655
1656 /* {{{ php_mysqlnd_rowp_read */
1657 static enum_func_status
php_mysqlnd_rowp_read(MYSQLND_CONN_DATA * conn,void * _packet)1658 php_mysqlnd_rowp_read(MYSQLND_CONN_DATA * conn, void * _packet)
1659 {
1660 MYSQLND_PACKET_ROW *packet = (MYSQLND_PACKET_ROW *) _packet;
1661 MYSQLND_ERROR_INFO * error_info = &packet->error_info;
1662 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1663 MYSQLND_VIO * vio = conn->vio;
1664 MYSQLND_STATS * stats = conn->stats;
1665 zend_uchar *p;
1666 enum_func_status ret = PASS;
1667 size_t data_size = 0;
1668
1669 DBG_ENTER("php_mysqlnd_rowp_read");
1670
1671 ret = php_mysqlnd_read_row_ex(pfc, vio, stats, error_info, &conn->state,
1672 packet->result_set_memory_pool, &packet->row_buffer, &data_size);
1673 if (FAIL == ret) {
1674 goto end;
1675 }
1676 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
1677 MYSQLND_HEADER_SIZE + packet->header.size,
1678 packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
1679 1);
1680
1681 /*
1682 packet->row_buffer->ptr is of size 'data_size'
1683 in pre-7.0 it was really 'data_size + 1' although it was counted as 'data_size'
1684 The +1 was for the additional byte needed to \0 terminate the last string in the row.
1685 This was needed as the zvals of pre-7.0 could use external memory (no copy param to ZVAL_STRINGL).
1686 However, in 7.0+ the strings always copy. Thus this +1 byte was removed. Also the optimization or \0
1687 terminating every string, which did overwrite the lengths from the packet. For this reason we needed
1688 to keep (and copy) the lengths externally.
1689 */
1690 packet->header.size = data_size;
1691 packet->row_buffer.size = data_size;
1692
1693 if (ERROR_MARKER == (*(p = packet->row_buffer.ptr))) {
1694 /*
1695 Error message as part of the result set,
1696 not good but we should not hang. See:
1697 Bug #27876 : SF with cyrillic variable name fails during execution
1698 */
1699 ret = FAIL;
1700 php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
1701 packet->error_info.error,
1702 sizeof(packet->error_info.error),
1703 &packet->error_info.error_no,
1704 packet->error_info.sqlstate
1705 );
1706 } else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
1707 packet->eof = TRUE;
1708 p++;
1709 if (data_size > 1) {
1710 packet->warning_count = uint2korr(p);
1711 p += 2;
1712 packet->server_status = uint2korr(p);
1713 /* Seems we have 3 bytes reserved for future use */
1714 DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
1715 }
1716 } else {
1717 packet->eof = FALSE;
1718 MYSQLND_INC_CONN_STATISTIC(stats,
1719 packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
1720 STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
1721 }
1722
1723 end:
1724 DBG_RETURN(ret);
1725 }
1726 /* }}} */
1727
1728
1729 /* {{{ php_mysqlnd_stats_read */
1730 static enum_func_status
php_mysqlnd_stats_read(MYSQLND_CONN_DATA * conn,void * _packet)1731 php_mysqlnd_stats_read(MYSQLND_CONN_DATA * conn, void * _packet)
1732 {
1733 MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
1734 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1735 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1736 MYSQLND_VIO * vio = conn->vio;
1737 MYSQLND_STATS * stats = conn->stats;
1738 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
1739 const size_t buf_len = pfc->cmd_buffer.length;
1740 zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
1741
1742 DBG_ENTER("php_mysqlnd_stats_read");
1743
1744 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "statistics", PROT_STATS_PACKET)) {
1745 DBG_RETURN(FAIL);
1746 }
1747
1748 packet->message.s = mnd_emalloc(packet->header.size + 1);
1749 memcpy(packet->message.s, buf, packet->header.size);
1750 packet->message.s[packet->header.size] = '\0';
1751 packet->message.l = packet->header.size;
1752
1753 DBG_RETURN(PASS);
1754 }
1755 /* }}} */
1756
1757
1758 /* {{{ php_mysqlnd_stats_free_mem */
1759 static
php_mysqlnd_stats_free_mem(void * _packet)1760 void php_mysqlnd_stats_free_mem(void * _packet)
1761 {
1762 MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
1763 mysqlnd_set_string(&p->message, NULL, 0);
1764 }
1765 /* }}} */
1766
1767
1768 /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
1769 #define PREPARE_RESPONSE_SIZE_41 9
1770 #define PREPARE_RESPONSE_SIZE_50 12
1771
1772 /* {{{ php_mysqlnd_prepare_read */
1773 static enum_func_status
php_mysqlnd_prepare_read(MYSQLND_CONN_DATA * conn,void * _packet)1774 php_mysqlnd_prepare_read(MYSQLND_CONN_DATA * conn, void * _packet)
1775 {
1776 MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
1777 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1778 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1779 MYSQLND_VIO * vio = conn->vio;
1780 MYSQLND_STATS * stats = conn->stats;
1781 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
1782 /* In case of an error, we should have place to put it */
1783 const size_t buf_len = pfc->cmd_buffer.length;
1784 zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
1785 zend_uchar *p = buf;
1786 const zend_uchar * const begin = buf;
1787 unsigned int data_size;
1788
1789 DBG_ENTER("php_mysqlnd_prepare_read");
1790
1791 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET)) {
1792 DBG_RETURN(FAIL);
1793 }
1794 BAIL_IF_NO_MORE_DATA;
1795
1796 data_size = packet->header.size;
1797 packet->error_code = uint1korr(p);
1798 p++;
1799 BAIL_IF_NO_MORE_DATA;
1800
1801 if (ERROR_MARKER == packet->error_code) {
1802 php_mysqlnd_read_error_from_line(p, data_size - 1,
1803 packet->error_info.error,
1804 sizeof(packet->error_info.error),
1805 &packet->error_info.error_no,
1806 packet->error_info.sqlstate
1807 );
1808 DBG_RETURN(PASS);
1809 }
1810
1811 if (data_size != PREPARE_RESPONSE_SIZE_41 &&
1812 data_size != PREPARE_RESPONSE_SIZE_50 &&
1813 !(data_size > PREPARE_RESPONSE_SIZE_50)) {
1814 DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size);
1815 php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size);
1816 DBG_RETURN(FAIL);
1817 }
1818
1819 packet->stmt_id = uint4korr(p);
1820 p += 4;
1821 BAIL_IF_NO_MORE_DATA;
1822
1823 /* Number of columns in result set */
1824 packet->field_count = uint2korr(p);
1825 p += 2;
1826 BAIL_IF_NO_MORE_DATA;
1827
1828 packet->param_count = uint2korr(p);
1829 p += 2;
1830 BAIL_IF_NO_MORE_DATA;
1831
1832 if (data_size > 9) {
1833 /* 0x0 filler sent by the server for 5.0+ clients */
1834 p++;
1835 BAIL_IF_NO_MORE_DATA;
1836
1837 packet->warning_count = uint2korr(p);
1838 }
1839
1840 DBG_INF_FMT("Prepare packet read: stmt_id=" ZEND_ULONG_FMT " fields=%u params=%u",
1841 packet->stmt_id, packet->field_count, packet->param_count);
1842
1843 BAIL_IF_NO_MORE_DATA;
1844
1845 DBG_RETURN(PASS);
1846 premature_end:
1847 DBG_ERR_FMT("PREPARE packet %zu bytes shorter than expected", p - begin - packet->header.size);
1848 php_error_docref(NULL, E_WARNING, "PREPARE packet %zu bytes shorter than expected",
1849 p - begin - packet->header.size);
1850 DBG_RETURN(FAIL);
1851 }
1852 /* }}} */
1853
1854
1855 /* {{{ php_mysqlnd_chg_user_read */
1856 static enum_func_status
php_mysqlnd_chg_user_read(MYSQLND_CONN_DATA * conn,void * _packet)1857 php_mysqlnd_chg_user_read(MYSQLND_CONN_DATA * conn, void * _packet)
1858 {
1859 MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
1860 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1861 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1862 MYSQLND_VIO * vio = conn->vio;
1863 MYSQLND_STATS * stats = conn->stats;
1864 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
1865 /* There could be an error message */
1866 const size_t buf_len = pfc->cmd_buffer.length;
1867 zend_uchar *buf = (zend_uchar *) pfc->cmd_buffer.buffer;
1868 zend_uchar *p = buf;
1869 const zend_uchar * const begin = buf;
1870
1871 DBG_ENTER("php_mysqlnd_chg_user_read");
1872
1873 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET)) {
1874 DBG_RETURN(FAIL);
1875 }
1876 BAIL_IF_NO_MORE_DATA;
1877
1878 /*
1879 Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
1880 of encoded sequence for length.
1881 */
1882
1883 /* Should be always 0x0 or ERROR_MARKER for error */
1884 packet->response_code = uint1korr(p);
1885 p++;
1886
1887 if (packet->header.size == 1 && buf[0] == EODATA_MARKER && (packet->server_capabilities & CLIENT_SECURE_CONNECTION)) {
1888 /* We don't handle 3.23 authentication */
1889 packet->server_asked_323_auth = TRUE;
1890 DBG_RETURN(FAIL);
1891 }
1892
1893 if (ERROR_MARKER == packet->response_code) {
1894 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1895 packet->error_info.error,
1896 sizeof(packet->error_info.error),
1897 &packet->error_info.error_no,
1898 packet->error_info.sqlstate
1899 );
1900 }
1901 BAIL_IF_NO_MORE_DATA;
1902 if (packet->response_code == 0xFE && packet->header.size > (size_t) (p - buf)) {
1903 packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
1904 packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
1905 p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
1906 packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
1907 if (packet->new_auth_protocol_data_len) {
1908 packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
1909 memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
1910 }
1911 DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
1912 DBG_INF_FMT("Server salt : [%*s]", (int) packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
1913 }
1914
1915 DBG_RETURN(PASS);
1916 premature_end:
1917 DBG_ERR_FMT("CHANGE_USER packet %zu bytes shorter than expected", p - begin - packet->header.size);
1918 php_error_docref(NULL, E_WARNING, "CHANGE_USER packet %zu bytes shorter than expected",
1919 p - begin - packet->header.size);
1920 DBG_RETURN(FAIL);
1921 }
1922 /* }}} */
1923
1924
1925 /* {{{ php_mysqlnd_chg_user_free_mem */
1926 static void
php_mysqlnd_chg_user_free_mem(void * _packet)1927 php_mysqlnd_chg_user_free_mem(void * _packet)
1928 {
1929 MYSQLND_PACKET_CHG_USER_RESPONSE * p = (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
1930
1931 if (p->new_auth_protocol) {
1932 mnd_efree(p->new_auth_protocol);
1933 p->new_auth_protocol = NULL;
1934 }
1935 p->new_auth_protocol_len = 0;
1936
1937 if (p->new_auth_protocol_data) {
1938 mnd_efree(p->new_auth_protocol_data);
1939 p->new_auth_protocol_data = NULL;
1940 }
1941 p->new_auth_protocol_data_len = 0;
1942 }
1943 /* }}} */
1944
1945
1946 /* {{{ php_mysqlnd_sha256_pk_request_write */
1947 static
php_mysqlnd_sha256_pk_request_write(MYSQLND_CONN_DATA * conn,void * _packet)1948 size_t php_mysqlnd_sha256_pk_request_write(MYSQLND_CONN_DATA * conn, void * _packet)
1949 {
1950 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1951 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1952 MYSQLND_VIO * vio = conn->vio;
1953 MYSQLND_STATS * stats = conn->stats;
1954 zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
1955 size_t sent;
1956
1957 DBG_ENTER("php_mysqlnd_sha256_pk_request_write");
1958
1959 int1store(buffer + MYSQLND_HEADER_SIZE, '\1');
1960 sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
1961
1962 DBG_RETURN(sent);
1963 }
1964 /* }}} */
1965
1966
1967 /* {{{ php_mysqlnd_sha256_pk_request_response_read */
1968 static enum_func_status
php_mysqlnd_sha256_pk_request_response_read(MYSQLND_CONN_DATA * conn,void * _packet)1969 php_mysqlnd_sha256_pk_request_response_read(MYSQLND_CONN_DATA * conn, void * _packet)
1970 {
1971 MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * packet= (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
1972 MYSQLND_ERROR_INFO * error_info = conn->error_info;
1973 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
1974 MYSQLND_VIO * vio = conn->vio;
1975 MYSQLND_STATS * stats = conn->stats;
1976 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
1977 const size_t buf_len = pfc->cmd_buffer.length;
1978 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
1979 const zend_uchar * p = buf;
1980 const zend_uchar * const begin = buf;
1981
1982 DBG_ENTER("php_mysqlnd_sha256_pk_request_response_read");
1983
1984 /* leave space for terminating safety \0 */
1985 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "SHA256_PK_REQUEST_RESPONSE", PROT_SHA256_PK_REQUEST_RESPONSE_PACKET)) {
1986 DBG_RETURN(FAIL);
1987 }
1988 BAIL_IF_NO_MORE_DATA;
1989
1990 p++;
1991 BAIL_IF_NO_MORE_DATA;
1992
1993 packet->public_key_len = packet->header.size - (p - buf);
1994 packet->public_key = mnd_emalloc(packet->public_key_len + 1);
1995 memcpy(packet->public_key, p, packet->public_key_len);
1996 packet->public_key[packet->public_key_len] = '\0';
1997
1998 DBG_RETURN(PASS);
1999
2000 premature_end:
2001 DBG_ERR_FMT("OK packet %zu bytes shorter than expected", p - begin - packet->header.size);
2002 php_error_docref(NULL, E_WARNING, "SHA256_PK_REQUEST_RESPONSE packet %zu bytes shorter than expected",
2003 p - begin - packet->header.size);
2004 DBG_RETURN(FAIL);
2005 }
2006 /* }}} */
2007
2008
2009 /* {{{ php_mysqlnd_sha256_pk_request_response_free_mem */
2010 static void
php_mysqlnd_sha256_pk_request_response_free_mem(void * _packet)2011 php_mysqlnd_sha256_pk_request_response_free_mem(void * _packet)
2012 {
2013 MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * p = (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
2014 if (p->public_key) {
2015 mnd_efree(p->public_key);
2016 p->public_key = NULL;
2017 }
2018 p->public_key_len = 0;
2019 }
2020 /* }}} */
2021
2022 static
php_mysqlnd_cached_sha2_result_write(MYSQLND_CONN_DATA * conn,void * _packet)2023 size_t php_mysqlnd_cached_sha2_result_write(MYSQLND_CONN_DATA * conn, void * _packet)
2024 {
2025 MYSQLND_PACKET_CACHED_SHA2_RESULT * packet= (MYSQLND_PACKET_CACHED_SHA2_RESULT *) _packet;
2026 MYSQLND_ERROR_INFO * error_info = conn->error_info;
2027 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
2028 MYSQLND_VIO * vio = conn->vio;
2029 MYSQLND_STATS * stats = conn->stats;
2030 ALLOCA_FLAG(use_heap)
2031 zend_uchar *buffer = do_alloca(MYSQLND_HEADER_SIZE + packet->password_len + 1, use_heap);
2032 size_t sent;
2033
2034 DBG_ENTER("php_mysqlnd_cached_sha2_result_write");
2035
2036 if (packet->request == 1) {
2037 int1store(buffer + MYSQLND_HEADER_SIZE, '\2');
2038 sent = pfc->data->m.send(pfc, vio, buffer, 1, stats, error_info);
2039 } else {
2040 if (packet->password_len != 0) {
2041 memcpy(buffer + MYSQLND_HEADER_SIZE, packet->password, packet->password_len);
2042 }
2043 sent = pfc->data->m.send(pfc, vio, buffer, packet->password_len, stats, error_info);
2044 }
2045
2046 free_alloca(buffer, use_heap);
2047 DBG_RETURN(sent);
2048 }
2049
2050 static enum_func_status
php_mysqlnd_cached_sha2_result_read(MYSQLND_CONN_DATA * conn,void * _packet)2051 php_mysqlnd_cached_sha2_result_read(MYSQLND_CONN_DATA * conn, void * _packet)
2052 {
2053 MYSQLND_PACKET_CACHED_SHA2_RESULT * packet= (MYSQLND_PACKET_CACHED_SHA2_RESULT *) _packet;
2054 MYSQLND_ERROR_INFO * error_info = conn->error_info;
2055 MYSQLND_PFC * pfc = conn->protocol_frame_codec;
2056 MYSQLND_VIO * vio = conn->vio;
2057 MYSQLND_STATS * stats = conn->stats;
2058 MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
2059 const size_t buf_len = pfc->cmd_buffer.length;
2060 zend_uchar * const buf = (zend_uchar *) pfc->cmd_buffer.buffer;
2061 const zend_uchar * p = buf;
2062 const zend_uchar * const begin = buf;
2063
2064 DBG_ENTER("php_mysqlnd_cached_sha2_result_read");
2065 if (FAIL == mysqlnd_read_packet_header_and_body(&(packet->header), pfc, vio, stats, error_info, connection_state, buf, buf_len, "PROT_CACHED_SHA2_RESULT_PACKET", PROT_CACHED_SHA2_RESULT_PACKET)) {
2066 DBG_RETURN(FAIL);
2067 }
2068 BAIL_IF_NO_MORE_DATA;
2069
2070 packet->response_code = uint1korr(p);
2071 p++;
2072 BAIL_IF_NO_MORE_DATA;
2073
2074 if (ERROR_MARKER == packet->response_code) {
2075 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
2076 packet->error, sizeof(packet->error),
2077 &packet->error_no, packet->sqlstate
2078 );
2079 DBG_RETURN(PASS);
2080 }
2081 if (0xFE == packet->response_code) {
2082 /* Authentication Switch Response */
2083 if (packet->header.size > (size_t) (p - buf)) {
2084 packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
2085 packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
2086 p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
2087
2088 packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
2089 if (packet->new_auth_protocol_data_len) {
2090 packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
2091 memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
2092 }
2093 DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
2094 DBG_INF_FMT("Server salt : [%zu][%.*s]", packet->new_auth_protocol_data_len, (int) packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
2095 }
2096 DBG_RETURN(PASS);
2097 }
2098
2099 if (0x1 != packet->response_code) {
2100 DBG_ERR_FMT("Unexpected response code %d", packet->response_code);
2101 }
2102
2103 /* This is not really the response code, but we reuse the field. */
2104 packet->response_code = uint1korr(p);
2105 p++;
2106 BAIL_IF_NO_MORE_DATA;
2107
2108 packet->result = uint1korr(p);
2109 BAIL_IF_NO_MORE_DATA;
2110
2111 DBG_RETURN(PASS);
2112
2113 premature_end:
2114 DBG_ERR_FMT("OK packet %zu bytes shorter than expected", p - begin - packet->header.size);
2115 php_error_docref(NULL, E_WARNING, "SHA256_PK_REQUEST_RESPONSE packet %zu bytes shorter than expected",
2116 p - begin - packet->header.size);
2117 DBG_RETURN(FAIL);
2118 }
2119
2120 /* {{{ packet_methods */
2121 static
2122 mysqlnd_packet_methods packet_methods[PROT_LAST] =
2123 {
2124 {
2125 php_mysqlnd_greet_read,
2126 NULL, /* write */
2127 php_mysqlnd_greet_free_mem,
2128 }, /* PROT_GREET_PACKET */
2129 {
2130 NULL, /* read */
2131 php_mysqlnd_auth_write,
2132 NULL,
2133 }, /* PROT_AUTH_PACKET */
2134 {
2135 php_mysqlnd_auth_response_read, /* read */
2136 NULL, /* write */
2137 php_mysqlnd_auth_response_free_mem,
2138 }, /* PROT_AUTH_RESP_PACKET */
2139 {
2140 NULL, /* read */
2141 php_mysqlnd_change_auth_response_write, /* write */
2142 NULL,
2143 }, /* PROT_CHANGE_AUTH_RESP_PACKET */
2144 {
2145 php_mysqlnd_ok_read, /* read */
2146 NULL, /* write */
2147 php_mysqlnd_ok_free_mem,
2148 }, /* PROT_OK_PACKET */
2149 {
2150 php_mysqlnd_eof_read, /* read */
2151 NULL, /* write */
2152 NULL,
2153 }, /* PROT_EOF_PACKET */
2154 {
2155 NULL, /* read */
2156 php_mysqlnd_cmd_write, /* write */
2157 NULL,
2158 }, /* PROT_CMD_PACKET */
2159 {
2160 php_mysqlnd_rset_header_read, /* read */
2161 NULL, /* write */
2162 php_mysqlnd_rset_header_free_mem,
2163 }, /* PROT_RSET_HEADER_PACKET */
2164 {
2165 php_mysqlnd_rset_field_read, /* read */
2166 NULL, /* write */
2167 NULL,
2168 }, /* PROT_RSET_FLD_PACKET */
2169 {
2170 php_mysqlnd_rowp_read, /* read */
2171 NULL, /* write */
2172 NULL,
2173 }, /* PROT_ROW_PACKET */
2174 {
2175 php_mysqlnd_stats_read, /* read */
2176 NULL, /* write */
2177 php_mysqlnd_stats_free_mem,
2178 }, /* PROT_STATS_PACKET */
2179 {
2180 php_mysqlnd_prepare_read, /* read */
2181 NULL, /* write */
2182 NULL,
2183 }, /* PROT_PREPARE_RESP_PACKET */
2184 {
2185 php_mysqlnd_chg_user_read, /* read */
2186 NULL, /* write */
2187 php_mysqlnd_chg_user_free_mem,
2188 }, /* PROT_CHG_USER_RESP_PACKET */
2189 {
2190 NULL, /* read */
2191 php_mysqlnd_sha256_pk_request_write,
2192 NULL,
2193 }, /* PROT_SHA256_PK_REQUEST_PACKET */
2194 {
2195 php_mysqlnd_sha256_pk_request_response_read,
2196 NULL, /* write */
2197 php_mysqlnd_sha256_pk_request_response_free_mem,
2198 }, /* PROT_SHA256_PK_REQUEST_RESPONSE_PACKET */
2199 {
2200 php_mysqlnd_cached_sha2_result_read,
2201 php_mysqlnd_cached_sha2_result_write,
2202 NULL
2203 } /* PROT_CACHED_SHA2_RESULT_PACKET */
2204 };
2205 /* }}} */
2206
2207
2208 /* {{{ mysqlnd_protocol::init_greet_packet */
2209 static void
MYSQLND_METHOD(mysqlnd_protocol,init_greet_packet)2210 MYSQLND_METHOD(mysqlnd_protocol, init_greet_packet)(struct st_mysqlnd_packet_greet *packet)
2211 {
2212 DBG_ENTER("mysqlnd_protocol::init_greet_packet");
2213 memset(packet, 0, sizeof(*packet));
2214 packet->header.m = &packet_methods[PROT_GREET_PACKET];
2215 DBG_VOID_RETURN;
2216 }
2217 /* }}} */
2218
2219
2220 /* {{{ mysqlnd_protocol::init_auth_packet */
2221 static void
MYSQLND_METHOD(mysqlnd_protocol,init_auth_packet)2222 MYSQLND_METHOD(mysqlnd_protocol, init_auth_packet)(struct st_mysqlnd_packet_auth *packet)
2223 {
2224 DBG_ENTER("mysqlnd_protocol::init_auth_packet");
2225 memset(packet, 0, sizeof(*packet));
2226 packet->header.m = &packet_methods[PROT_AUTH_PACKET];
2227 DBG_VOID_RETURN;
2228 }
2229 /* }}} */
2230
2231
2232 /* {{{ mysqlnd_protocol::init_auth_response_packet */
2233 static void
MYSQLND_METHOD(mysqlnd_protocol,init_auth_response_packet)2234 MYSQLND_METHOD(mysqlnd_protocol, init_auth_response_packet)(struct st_mysqlnd_packet_auth_response *packet)
2235 {
2236 DBG_ENTER("mysqlnd_protocol::init_auth_response_packet");
2237 memset(packet, 0, sizeof(*packet));
2238 packet->header.m = &packet_methods[PROT_AUTH_RESP_PACKET];
2239 DBG_VOID_RETURN;
2240 }
2241 /* }}} */
2242
2243
2244 /* {{{ mysqlnd_protocol::init_change_auth_response_packet */
2245 static void
MYSQLND_METHOD(mysqlnd_protocol,init_change_auth_response_packet)2246 MYSQLND_METHOD(mysqlnd_protocol, init_change_auth_response_packet)(struct st_mysqlnd_packet_change_auth_response *packet)
2247 {
2248 DBG_ENTER("mysqlnd_protocol::init_change_auth_response_packet");
2249 memset(packet, 0, sizeof(*packet));
2250 packet->header.m = &packet_methods[PROT_CHANGE_AUTH_RESP_PACKET];
2251 DBG_VOID_RETURN;
2252 }
2253 /* }}} */
2254
2255
2256 /* {{{ mysqlnd_protocol::init_ok_packet */
2257 static void
MYSQLND_METHOD(mysqlnd_protocol,init_ok_packet)2258 MYSQLND_METHOD(mysqlnd_protocol, init_ok_packet)(struct st_mysqlnd_packet_ok *packet)
2259 {
2260 DBG_ENTER("mysqlnd_protocol::init_ok_packet");
2261 memset(packet, 0, sizeof(*packet));
2262 packet->header.m = &packet_methods[PROT_OK_PACKET];
2263 DBG_VOID_RETURN;
2264 }
2265 /* }}} */
2266
2267
2268 /* {{{ mysqlnd_protocol::init_eof_packet */
2269 static void
MYSQLND_METHOD(mysqlnd_protocol,init_eof_packet)2270 MYSQLND_METHOD(mysqlnd_protocol, init_eof_packet)(struct st_mysqlnd_packet_eof *packet)
2271 {
2272 DBG_ENTER("mysqlnd_protocol::init_eof_packet");
2273 memset(packet, 0, sizeof(*packet));
2274 packet->header.m = &packet_methods[PROT_EOF_PACKET];
2275 DBG_VOID_RETURN;
2276 }
2277 /* }}} */
2278
2279
2280 /* {{{ mysqlnd_protocol::init_command_packet */
2281 static void
MYSQLND_METHOD(mysqlnd_protocol,init_command_packet)2282 MYSQLND_METHOD(mysqlnd_protocol, init_command_packet)(struct st_mysqlnd_packet_command *packet)
2283 {
2284 DBG_ENTER("mysqlnd_protocol::init_command_packet");
2285 memset(packet, 0, sizeof(*packet));
2286 packet->header.m = &packet_methods[PROT_CMD_PACKET];
2287 DBG_VOID_RETURN;
2288 }
2289 /* }}} */
2290
2291
2292 /* {{{ mysqlnd_protocol::init_rset_packet */
2293 static void
MYSQLND_METHOD(mysqlnd_protocol,init_rset_header_packet)2294 MYSQLND_METHOD(mysqlnd_protocol, init_rset_header_packet)(struct st_mysqlnd_packet_rset_header *packet)
2295 {
2296 DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
2297 memset(packet, 0, sizeof(*packet));
2298 packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
2299 DBG_VOID_RETURN;
2300 }
2301 /* }}} */
2302
2303
2304 /* {{{ mysqlnd_protocol::init_result_field_packet */
2305 static void
MYSQLND_METHOD(mysqlnd_protocol,init_result_field_packet)2306 MYSQLND_METHOD(mysqlnd_protocol, init_result_field_packet)(struct st_mysqlnd_packet_res_field *packet)
2307 {
2308 DBG_ENTER("mysqlnd_protocol::init_result_field_packet");
2309 memset(packet, 0, sizeof(*packet));
2310 packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
2311 DBG_VOID_RETURN;
2312 }
2313 /* }}} */
2314
2315
2316 /* {{{ mysqlnd_protocol::init_row_packet */
2317 static void
MYSQLND_METHOD(mysqlnd_protocol,init_row_packet)2318 MYSQLND_METHOD(mysqlnd_protocol, init_row_packet)(struct st_mysqlnd_packet_row *packet)
2319 {
2320 DBG_ENTER("mysqlnd_protocol::init_row_packet");
2321 memset(packet, 0, sizeof(*packet));
2322 packet->header.m = &packet_methods[PROT_ROW_PACKET];
2323 DBG_VOID_RETURN;
2324 }
2325 /* }}} */
2326
2327
2328 /* {{{ mysqlnd_protocol::init_stats_packet */
2329 static void
MYSQLND_METHOD(mysqlnd_protocol,init_stats_packet)2330 MYSQLND_METHOD(mysqlnd_protocol, init_stats_packet)(struct st_mysqlnd_packet_stats *packet)
2331 {
2332 DBG_ENTER("mysqlnd_protocol::init_stats_packet");
2333 memset(packet, 0, sizeof(*packet));
2334 packet->header.m = &packet_methods[PROT_STATS_PACKET];
2335 DBG_VOID_RETURN;
2336 }
2337 /* }}} */
2338
2339
2340 /* {{{ mysqlnd_protocol::init_prepare_response_packet */
2341 static void
MYSQLND_METHOD(mysqlnd_protocol,init_prepare_response_packet)2342 MYSQLND_METHOD(mysqlnd_protocol, init_prepare_response_packet)(struct st_mysqlnd_packet_prepare_response *packet)
2343 {
2344 DBG_ENTER("mysqlnd_protocol::init_prepare_response_packet");
2345 memset(packet, 0, sizeof(*packet));
2346 packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
2347 DBG_VOID_RETURN;
2348 }
2349 /* }}} */
2350
2351
2352 /* {{{ mysqlnd_protocol::init_change_user_response_packet */
2353 static void
MYSQLND_METHOD(mysqlnd_protocol,init_change_user_response_packet)2354 MYSQLND_METHOD(mysqlnd_protocol, init_change_user_response_packet)(struct st_mysqlnd_packet_chg_user_resp *packet)
2355 {
2356 DBG_ENTER("mysqlnd_protocol::init_change_user_response_packet");
2357 memset(packet, 0, sizeof(*packet));
2358 packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
2359 DBG_VOID_RETURN;
2360 }
2361 /* }}} */
2362
2363
2364 /* {{{ mysqlnd_protocol::init_sha256_pk_request_packet */
2365 static void
MYSQLND_METHOD(mysqlnd_protocol,init_sha256_pk_request_packet)2366 MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_packet)(struct st_mysqlnd_packet_sha256_pk_request *packet)
2367 {
2368 DBG_ENTER("mysqlnd_protocol::init_sha256_pk_request_packet");
2369 memset(packet, 0, sizeof(*packet));
2370 packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_PACKET];
2371 DBG_VOID_RETURN;
2372 }
2373 /* }}} */
2374
2375
2376 /* {{{ mysqlnd_protocol::init_sha256_pk_request_response_packet */
2377 static void
MYSQLND_METHOD(mysqlnd_protocol,init_sha256_pk_request_response_packet)2378 MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_response_packet)(struct st_mysqlnd_packet_sha256_pk_request_response *packet)
2379 {
2380 DBG_ENTER("mysqlnd_protocol::init_sha256_pk_request_response_packet");
2381 memset(packet, 0, sizeof(*packet));
2382 packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET];
2383 DBG_VOID_RETURN;
2384 }
2385 /* }}} */
2386
2387 /* {{{ mysqlnd_protocol::init_cached_sha2_result_packet */
2388 static void
MYSQLND_METHOD(mysqlnd_protocol,init_cached_sha2_result_packet)2389 MYSQLND_METHOD(mysqlnd_protocol, init_cached_sha2_result_packet)(struct st_mysqlnd_packet_cached_sha2_result *packet)
2390 {
2391 DBG_ENTER("mysqlnd_protocol::init_cached_sha2_result_packet");
2392 memset(packet, 0, sizeof(*packet));
2393 packet->header.m = &packet_methods[PROT_CACHED_SHA2_RESULT_PACKET];
2394 DBG_VOID_RETURN;
2395 }
2396 /* }}} */
2397
2398
2399 /* {{{ mysqlnd_protocol::send_command */
2400 static enum_func_status
MYSQLND_METHOD(mysqlnd_protocol,send_command)2401 MYSQLND_METHOD(mysqlnd_protocol, send_command)(
2402 MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
2403 const enum php_mysqlnd_server_command command,
2404 const zend_uchar * const arg, const size_t arg_len,
2405 const bool silent,
2406
2407 struct st_mysqlnd_connection_state * connection_state,
2408 MYSQLND_ERROR_INFO * error_info,
2409 MYSQLND_UPSERT_STATUS * upsert_status,
2410 MYSQLND_STATS * stats,
2411 func_mysqlnd_conn_data__send_close send_close,
2412 void * send_close_ctx)
2413 {
2414 enum_func_status ret = PASS;
2415 MYSQLND_PACKET_COMMAND cmd_packet;
2416 enum mysqlnd_connection_state state;
2417 DBG_ENTER("mysqlnd_protocol::send_command");
2418 DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
2419 DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(upsert_status));
2420 DBG_INF_FMT("sending %zu bytes", arg_len + 1); /* + 1 is for the command */
2421 state = connection_state->m->get(connection_state);
2422
2423 switch (state) {
2424 case CONN_READY:
2425 break;
2426 case CONN_QUIT_SENT:
2427 SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
2428 DBG_ERR("Server is gone");
2429 DBG_RETURN(FAIL);
2430 default:
2431 SET_CLIENT_ERROR(error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2432 DBG_ERR_FMT("Command out of sync. State=%u", state);
2433 DBG_RETURN(FAIL);
2434 }
2435
2436 UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
2437 SET_EMPTY_ERROR(error_info);
2438
2439 payload_decoder_factory->m.init_command_packet(&cmd_packet);
2440
2441 cmd_packet.command = command;
2442 if (arg && arg_len) {
2443 cmd_packet.argument.s = (char *) arg;
2444 cmd_packet.argument.l = arg_len;
2445 }
2446
2447 MYSQLND_INC_CONN_STATISTIC(stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
2448
2449 if (! PACKET_WRITE(payload_decoder_factory->conn, &cmd_packet)) {
2450 if (!silent && error_info->error_no != CR_SERVER_GONE_ERROR) {
2451 DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
2452 php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
2453 }
2454 connection_state->m->set(connection_state, CONN_QUIT_SENT);
2455 send_close(send_close_ctx);
2456 DBG_ERR("Server is gone");
2457 ret = FAIL;
2458 }
2459 PACKET_FREE(&cmd_packet);
2460 DBG_RETURN(ret);
2461 }
2462 /* }}} */
2463
2464
2465 /* {{{ mysqlnd_protocol::send_command_handle_OK */
2466 static enum_func_status
MYSQLND_METHOD(mysqlnd_protocol,send_command_handle_OK)2467 MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_OK)(
2468 MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
2469 MYSQLND_ERROR_INFO * const error_info,
2470 MYSQLND_UPSERT_STATUS * const upsert_status,
2471 const bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
2472 MYSQLND_STRING * const last_message)
2473 {
2474 enum_func_status ret = FAIL;
2475 MYSQLND_PACKET_OK ok_response;
2476
2477 payload_decoder_factory->m.init_ok_packet(&ok_response);
2478 DBG_ENTER("mysqlnd_protocol::send_command_handle_OK");
2479 if (FAIL == (ret = PACKET_READ(payload_decoder_factory->conn, &ok_response))) {
2480 if (error_info->error_no != CR_SERVER_GONE_ERROR) {
2481 DBG_INF("Error while reading OK packet");
2482 SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
2483 }
2484 goto end;
2485 }
2486 DBG_INF_FMT("OK from server");
2487 if (0xFF == ok_response.field_count) {
2488 /* The server signalled error. Set the error */
2489 SET_CLIENT_ERROR(error_info, ok_response.error_no, ok_response.sqlstate, ok_response.error);
2490 ret = FAIL;
2491 /*
2492 Cover a protocol design error: error packet does not
2493 contain the server status. Therefore, the client has no way
2494 to find out whether there are more result sets of
2495 a multiple-result-set statement pending. Luckily, in 5.0 an
2496 error always aborts execution of a statement, wherever it is
2497 a multi-statement or a stored procedure, so it should be
2498 safe to unconditionally turn off the flag here.
2499 */
2500 upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
2501 UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
2502 } else {
2503 mysqlnd_set_string(last_message, ok_response.message, ok_response.message_len);
2504 if (!ignore_upsert_status) {
2505 UPSERT_STATUS_RESET(upsert_status);
2506 UPSERT_STATUS_SET_WARNINGS(upsert_status, ok_response.warning_count);
2507 UPSERT_STATUS_SET_SERVER_STATUS(upsert_status, ok_response.server_status);
2508 UPSERT_STATUS_SET_AFFECTED_ROWS(upsert_status, ok_response.affected_rows);
2509 UPSERT_STATUS_SET_LAST_INSERT_ID(upsert_status, ok_response.last_insert_id);
2510 } else {
2511 /* LOAD DATA */
2512 }
2513 }
2514 end:
2515 PACKET_FREE(&ok_response);
2516 DBG_INF(ret == PASS ? "PASS":"FAIL");
2517 DBG_RETURN(ret);
2518 }
2519 /* }}} */
2520
2521
2522 /* {{{ mysqlnd_protocol::send_command_handle_EOF */
2523 static enum_func_status
MYSQLND_METHOD(mysqlnd_protocol,send_command_handle_EOF)2524 MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_EOF)(
2525 MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const payload_decoder_factory,
2526 MYSQLND_ERROR_INFO * const error_info,
2527 MYSQLND_UPSERT_STATUS * const upsert_status)
2528 {
2529 enum_func_status ret = FAIL;
2530 MYSQLND_PACKET_EOF response;
2531
2532 payload_decoder_factory->m.init_eof_packet(&response);
2533
2534 DBG_ENTER("mysqlnd_protocol::send_command_handle_EOF");
2535
2536 if (FAIL == (ret = PACKET_READ(payload_decoder_factory->conn, &response))) {
2537 DBG_INF("Error while reading EOF packet");
2538 SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
2539 } else if (0xFF == response.field_count) {
2540 /* The server signalled error. Set the error */
2541 DBG_INF_FMT("Error_no=%d SQLstate=%s Error=%s", response.error_no, response.sqlstate, response.error);
2542
2543 SET_CLIENT_ERROR(error_info, response.error_no, response.sqlstate, response.error);
2544
2545 UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(upsert_status);
2546 } else if (0xFE != response.field_count) {
2547 SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
2548 DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", response.field_count);
2549 php_error_docref(NULL, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X", response.field_count);
2550 } else {
2551 DBG_INF_FMT("EOF from server");
2552 }
2553 PACKET_FREE(&response);
2554
2555 DBG_INF(ret == PASS ? "PASS":"FAIL");
2556 DBG_RETURN(ret);
2557 }
2558 /* }}} */
2559
2560
2561 /* {{{ send_command_handle_response */
2562 static enum_func_status
MYSQLND_METHOD(mysqlnd_protocol,send_command_handle_response)2563 MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response)(
2564 MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * payload_decoder_factory,
2565 const enum mysqlnd_packet_type ok_packet,
2566 const bool silent,
2567 const enum php_mysqlnd_server_command command,
2568 const bool ignore_upsert_status, /* actually used only by LOAD DATA. COM_QUERY and COM_EXECUTE handle the responses themselves */
2569
2570 MYSQLND_ERROR_INFO * error_info,
2571 MYSQLND_UPSERT_STATUS * upsert_status,
2572 MYSQLND_STRING * last_message
2573 )
2574 {
2575 enum_func_status ret = FAIL;
2576
2577 DBG_ENTER("mysqlnd_protocol::send_command_handle_response");
2578 DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
2579
2580 switch (ok_packet) {
2581 case PROT_OK_PACKET:
2582 ret = payload_decoder_factory->m.send_command_handle_OK(payload_decoder_factory, error_info, upsert_status, ignore_upsert_status, last_message);
2583 break;
2584 case PROT_EOF_PACKET:
2585 ret = payload_decoder_factory->m.send_command_handle_EOF(payload_decoder_factory, error_info, upsert_status);
2586 break;
2587 default:
2588 SET_CLIENT_ERROR(error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
2589 php_error_docref(NULL, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
2590 break;
2591 }
2592 if (!silent && error_info->error_no == CR_MALFORMED_PACKET) {
2593 php_error_docref(NULL, E_WARNING, "Error while reading %s's response packet. PID=%d", mysqlnd_command_to_text[command], getpid());
2594 }
2595 DBG_INF(ret == PASS ? "PASS":"FAIL");
2596 DBG_RETURN(ret);
2597 }
2598 /* }}} */
2599
2600
2601 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_payload_decoder_factory)
2602 MYSQLND_METHOD(mysqlnd_protocol, init_greet_packet),
2603 MYSQLND_METHOD(mysqlnd_protocol, init_auth_packet),
2604 MYSQLND_METHOD(mysqlnd_protocol, init_auth_response_packet),
2605 MYSQLND_METHOD(mysqlnd_protocol, init_change_auth_response_packet),
2606 MYSQLND_METHOD(mysqlnd_protocol, init_ok_packet),
2607 MYSQLND_METHOD(mysqlnd_protocol, init_command_packet),
2608 MYSQLND_METHOD(mysqlnd_protocol, init_eof_packet),
2609 MYSQLND_METHOD(mysqlnd_protocol, init_rset_header_packet),
2610 MYSQLND_METHOD(mysqlnd_protocol, init_result_field_packet),
2611 MYSQLND_METHOD(mysqlnd_protocol, init_row_packet),
2612 MYSQLND_METHOD(mysqlnd_protocol, init_stats_packet),
2613 MYSQLND_METHOD(mysqlnd_protocol, init_prepare_response_packet),
2614 MYSQLND_METHOD(mysqlnd_protocol, init_change_user_response_packet),
2615 MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_packet),
2616 MYSQLND_METHOD(mysqlnd_protocol, init_sha256_pk_request_response_packet),
2617 MYSQLND_METHOD(mysqlnd_protocol, init_cached_sha2_result_packet),
2618
2619 MYSQLND_METHOD(mysqlnd_protocol, send_command),
2620 MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response),
2621 MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_OK),
2622 MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_EOF),
2623 MYSQLND_CLASS_METHODS_END;
2624
2625
2626 /* {{{ mysqlnd_protocol_payload_decoder_factory_init */
2627 PHPAPI MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY *
mysqlnd_protocol_payload_decoder_factory_init(MYSQLND_CONN_DATA * conn,const bool persistent)2628 mysqlnd_protocol_payload_decoder_factory_init(MYSQLND_CONN_DATA * conn, const bool persistent)
2629 {
2630 MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * ret;
2631 DBG_ENTER("mysqlnd_protocol_payload_decoder_factory_init");
2632 ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_payload_decoder_factory(conn, persistent);
2633 DBG_RETURN(ret);
2634 }
2635 /* }}} */
2636
2637
2638 /* {{{ mysqlnd_protocol_payload_decoder_factory_free */
2639 PHPAPI void
mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory)2640 mysqlnd_protocol_payload_decoder_factory_free(MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * const factory)
2641 {
2642 DBG_ENTER("mysqlnd_protocol_payload_decoder_factory_free");
2643
2644 if (factory) {
2645 bool pers = factory->persistent;
2646 mnd_pefree(factory, pers);
2647 }
2648 DBG_VOID_RETURN;
2649 }
2650 /* }}} */
2651