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