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