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