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