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