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