1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2013 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: Georg Richter <georg@mysql.com> |
16 | Andrey Hristov <andrey@mysql.com> |
17 | Ulf Wendel <uwendel@mysql.com> |
18 +----------------------------------------------------------------------+
19 */
20 #include "php.h"
21 #include "php_globals.h"
22 #include "mysqlnd.h"
23 #include "mysqlnd_priv.h"
24 #include "mysqlnd_wireprotocol.h"
25 #include "mysqlnd_statistics.h"
26 #include "mysqlnd_debug.h"
27 #include "ext/standard/sha1.h"
28 #include "zend_ini.h"
29
30 #define MYSQLND_SILENT 1
31
32 #define MYSQLND_DUMP_HEADER_N_BODY
33
34 #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
35 { \
36 DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
37 if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
38 CONN_SET_STATE(conn, CONN_QUIT_SENT); \
39 SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
40 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
41 DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
42 DBG_RETURN(FAIL);\
43 }\
44 if ((buf_size) < (packet)->header.size) { \
45 DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
46 (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
47 DBG_RETURN(FAIL); \
48 }\
49 if (FAIL == conn->net->m.receive((conn), (buf), (packet)->header.size TSRMLS_CC)) { \
50 CONN_SET_STATE(conn, CONN_QUIT_SENT); \
51 SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
52 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
53 DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
54 DBG_RETURN(FAIL);\
55 } \
56 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
57 MYSQLND_HEADER_SIZE + (packet)->header.size, \
58 packet_type_to_statistic_packet_count[packet_type], \
59 1); \
60 }
61
62
63 #define BAIL_IF_NO_MORE_DATA \
64 if ((size_t)(p - begin) > packet->header.size) { \
65 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \
66 goto premature_end; \
67 } \
68
69
70 static const char *unknown_sqlstate= "HY000";
71
72 const char * const mysqlnd_empty_string = "";
73
74 /* Used in mysqlnd_debug.c */
75 const char mysqlnd_read_header_name[] = "mysqlnd_read_header";
76 const char mysqlnd_read_body_name[] = "mysqlnd_read_body";
77
78
79 #define ERROR_MARKER 0xFF
80 #define EODATA_MARKER 0xFE
81
82 /* {{{ mysqlnd_command_to_text
83 */
84 const char * const mysqlnd_command_to_text[COM_END] =
85 {
86 "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
87 "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
88 "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
89 "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
90 "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
91 "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
92 "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
93 };
94 /* }}} */
95
96
97
98 static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
99 {
100 STAT_LAST,
101 STAT_LAST,
102 STAT_BYTES_RECEIVED_OK,
103 STAT_BYTES_RECEIVED_EOF,
104 STAT_LAST,
105 STAT_BYTES_RECEIVED_RSET_HEADER,
106 STAT_BYTES_RECEIVED_RSET_FIELD_META,
107 STAT_BYTES_RECEIVED_RSET_ROW,
108 STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
109 STAT_BYTES_RECEIVED_CHANGE_USER,
110 };
111
112 static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
113 {
114 STAT_LAST,
115 STAT_LAST,
116 STAT_PACKETS_RECEIVED_OK,
117 STAT_PACKETS_RECEIVED_EOF,
118 STAT_LAST,
119 STAT_PACKETS_RECEIVED_RSET_HEADER,
120 STAT_PACKETS_RECEIVED_RSET_FIELD_META,
121 STAT_PACKETS_RECEIVED_RSET_ROW,
122 STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
123 STAT_PACKETS_RECEIVED_CHANGE_USER,
124 };
125
126
127 /* {{{ php_mysqlnd_net_field_length
128 Get next field's length */
129 unsigned long
php_mysqlnd_net_field_length(zend_uchar ** packet)130 php_mysqlnd_net_field_length(zend_uchar **packet)
131 {
132 register zend_uchar *p= (zend_uchar *)*packet;
133
134 if (*p < 251) {
135 (*packet)++;
136 return (unsigned long) *p;
137 }
138
139 switch (*p) {
140 case 251:
141 (*packet)++;
142 return MYSQLND_NULL_LENGTH;
143 case 252:
144 (*packet) += 3;
145 return (unsigned long) uint2korr(p+1);
146 case 253:
147 (*packet) += 4;
148 return (unsigned long) uint3korr(p+1);
149 default:
150 (*packet) += 9;
151 return (unsigned long) uint4korr(p+1);
152 }
153 }
154 /* }}} */
155
156
157 /* {{{ php_mysqlnd_net_field_length_ll
158 Get next field's length */
159 uint64_t
php_mysqlnd_net_field_length_ll(zend_uchar ** packet)160 php_mysqlnd_net_field_length_ll(zend_uchar **packet)
161 {
162 register zend_uchar *p= (zend_uchar *)*packet;
163
164 if (*p < 251) {
165 (*packet)++;
166 return (uint64_t) *p;
167 }
168
169 switch (*p) {
170 case 251:
171 (*packet)++;
172 return (uint64_t) MYSQLND_NULL_LENGTH;
173 case 252:
174 (*packet) += 3;
175 return (uint64_t) uint2korr(p + 1);
176 case 253:
177 (*packet) += 4;
178 return (uint64_t) uint3korr(p + 1);
179 default:
180 (*packet) += 9;
181 return (uint64_t) uint8korr(p + 1);
182 }
183 }
184 /* }}} */
185
186
187 /* {{{ php_mysqlnd_net_store_length */
188 zend_uchar *
php_mysqlnd_net_store_length(zend_uchar * packet,uint64_t length)189 php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
190 {
191 if (length < (uint64_t) L64(251)) {
192 *packet = (zend_uchar) length;
193 return packet + 1;
194 }
195
196 if (length < (uint64_t) L64(65536)) {
197 *packet++ = 252;
198 int2store(packet,(unsigned int) length);
199 return packet + 2;
200 }
201
202 if (length < (uint64_t) L64(16777216)) {
203 *packet++ = 253;
204 int3store(packet,(ulong) length);
205 return packet + 3;
206 }
207 *packet++ = 254;
208 int8store(packet, length);
209 return packet + 8;
210 }
211 /* }}} */
212
213
214 /* {{{ php_mysqlnd_read_error_from_line */
215 static enum_func_status
php_mysqlnd_read_error_from_line(zend_uchar * buf,size_t buf_len,char * error,int error_buf_len,unsigned int * error_no,char * sqlstate TSRMLS_DC)216 php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
217 char *error, int error_buf_len,
218 unsigned int *error_no, char *sqlstate TSRMLS_DC)
219 {
220 zend_uchar *p = buf;
221 int error_msg_len= 0;
222
223 DBG_ENTER("php_mysqlnd_read_error_from_line");
224
225 *error_no = CR_UNKNOWN_ERROR;
226 memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
227
228 if (buf_len > 2) {
229 *error_no = uint2korr(p);
230 p+= 2;
231 /*
232 sqlstate is following. No need to check for buf_left_len as we checked > 2 above,
233 if it was >=2 then we would need a check
234 */
235 if (*p == '#') {
236 ++p;
237 if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) {
238 memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH);
239 p+= MYSQLND_SQLSTATE_LENGTH;
240 } else {
241 goto end;
242 }
243 }
244 if ((buf_len - (p - buf)) > 0) {
245 error_msg_len = MIN((int)((buf_len - (p - buf))), (int) (error_buf_len - 1));
246 memcpy(error, p, error_msg_len);
247 }
248 }
249 end:
250 sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
251 error[error_msg_len]= '\0';
252
253 DBG_RETURN(FAIL);
254 }
255 /* }}} */
256
257
258 /* {{{ mysqlnd_read_header */
259 static enum_func_status
mysqlnd_read_header(MYSQLND * conn,MYSQLND_PACKET_HEADER * header TSRMLS_DC)260 mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC)
261 {
262 MYSQLND_NET * net = conn->net;
263 zend_uchar buffer[MYSQLND_HEADER_SIZE];
264
265 DBG_ENTER("mysqlnd_read_header_name");
266 DBG_INF_FMT("compressed=%u conn_id=%u", net->compressed, conn->thread_id);
267 if (FAIL == net->m.receive(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
268 DBG_RETURN(FAIL);
269 }
270
271 header->size = uint3korr(buffer);
272 header->packet_no = uint1korr(buffer + 3);
273
274 #ifdef MYSQLND_DUMP_HEADER_N_BODY
275 DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
276 #endif
277 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats,
278 STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
279 STAT_PACKETS_RECEIVED, 1);
280
281 if (net->compressed || net->packet_no == header->packet_no) {
282 /*
283 Have to increase the number, so we can send correct number back. It will
284 round at 255 as this is unsigned char. The server needs this for simple
285 flow control checking.
286 */
287 net->packet_no++;
288 DBG_RETURN(PASS);
289 }
290
291 DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
292 net->packet_no, header->packet_no, header->size);
293
294 php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
295 net->packet_no, header->packet_no, header->size);
296 DBG_RETURN(FAIL);
297 }
298 /* }}} */
299
300
301 /* {{{ php_mysqlnd_greet_read */
302 static enum_func_status
php_mysqlnd_greet_read(void * _packet,MYSQLND * conn TSRMLS_DC)303 php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
304 {
305 zend_uchar buf[2048];
306 zend_uchar *p = buf;
307 zend_uchar *begin = buf;
308 MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
309
310 DBG_ENTER("php_mysqlnd_greet_read");
311
312 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
313 BAIL_IF_NO_MORE_DATA;
314
315 packet->protocol_version = uint1korr(p);
316 p++;
317 BAIL_IF_NO_MORE_DATA;
318
319 if (ERROR_MARKER == packet->protocol_version) {
320 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
321 packet->error, sizeof(packet->error),
322 &packet->error_no, packet->sqlstate
323 TSRMLS_CC);
324 /*
325 The server doesn't send sqlstate in the greet packet.
326 It's a bug#26426 , so we have to set it correctly ourselves.
327 It's probably "Too many connections, which has SQL state 08004".
328 */
329 if (packet->error_no == 1040) {
330 memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
331 }
332 DBG_RETURN(PASS);
333 }
334
335 packet->server_version = estrdup((char *)p);
336 p+= strlen(packet->server_version) + 1; /* eat the '\0' */
337 BAIL_IF_NO_MORE_DATA;
338
339 packet->thread_id = uint4korr(p);
340 p+=4;
341 BAIL_IF_NO_MORE_DATA;
342
343 memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
344 p+= 8;
345 BAIL_IF_NO_MORE_DATA;
346
347 /* pad1 */
348 p++;
349 BAIL_IF_NO_MORE_DATA;
350
351 packet->server_capabilities = uint2korr(p);
352 p+= 2;
353 BAIL_IF_NO_MORE_DATA;
354
355 packet->charset_no = uint1korr(p);
356 p++;
357 BAIL_IF_NO_MORE_DATA;
358
359 packet->server_status = uint2korr(p);
360 p+= 2;
361 BAIL_IF_NO_MORE_DATA;
362
363 /* pad2 */
364 p+= 13;
365 BAIL_IF_NO_MORE_DATA;
366
367 if ((size_t) (p - buf) < packet->header.size) {
368 /* scramble_buf is split into two parts */
369 memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
370 p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
371 } else {
372 packet->pre41 = TRUE;
373 }
374
375 DBG_INF_FMT("proto=%u server=%s thread_id=%u",
376 packet->protocol_version, packet->server_version, packet->thread_id);
377
378 DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i",
379 packet->server_capabilities, packet->charset_no, packet->server_status);
380
381 DBG_RETURN(PASS);
382 premature_end:
383 DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
384 php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
385 p - begin - packet->header.size);
386 DBG_RETURN(FAIL);
387 }
388 /* }}} */
389
390
391 /* {{{ php_mysqlnd_greet_free_mem */
392 static
php_mysqlnd_greet_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)393 void php_mysqlnd_greet_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
394 {
395 MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet;
396 if (p->server_version) {
397 efree(p->server_version);
398 p->server_version = NULL;
399 }
400 if (!stack_allocation) {
401 mnd_pefree(p, p->header.persistent);
402 }
403 }
404 /* }}} */
405
406
407 /* {{{ php_mysqlnd_crypt */
408 static void
php_mysqlnd_crypt(zend_uchar * buffer,const zend_uchar * s1,const zend_uchar * s2,size_t len)409 php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
410 {
411 const zend_uchar *s1_end = s1 + len;
412 while (s1 < s1_end) {
413 *buffer++= *s1++ ^ *s2++;
414 }
415 }
416 /* }}} */
417
418
419 /* {{{ php_mysqlnd_scramble */
php_mysqlnd_scramble(zend_uchar * const buffer,const zend_uchar * const scramble,const zend_uchar * const password)420 void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password)
421 {
422 PHP_SHA1_CTX context;
423 zend_uchar sha1[SHA1_MAX_LENGTH];
424 zend_uchar sha2[SHA1_MAX_LENGTH];
425
426
427 /* Phase 1: hash password */
428 PHP_SHA1Init(&context);
429 PHP_SHA1Update(&context, password, strlen((char *)password));
430 PHP_SHA1Final(sha1, &context);
431
432 /* Phase 2: hash sha1 */
433 PHP_SHA1Init(&context);
434 PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
435 PHP_SHA1Final(sha2, &context);
436
437 /* Phase 3: hash scramble + sha2 */
438 PHP_SHA1Init(&context);
439 PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
440 PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
441 PHP_SHA1Final(buffer, &context);
442
443 /* let's crypt buffer now */
444 php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
445 }
446 /* }}} */
447
448
449 #define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SHA1_MAX_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 128)
450
451 /* {{{ php_mysqlnd_auth_write */
452 static
php_mysqlnd_auth_write(void * _packet,MYSQLND * conn TSRMLS_DC)453 size_t php_mysqlnd_auth_write(void *_packet, MYSQLND * conn TSRMLS_DC)
454 {
455 char buffer[AUTH_WRITE_BUFFER_LEN];
456 register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
457 int len;
458 register MYSQLND_PACKET_AUTH *packet= (MYSQLND_PACKET_AUTH *) _packet;
459
460 DBG_ENTER("php_mysqlnd_auth_write");
461
462 int4store(p, packet->client_flags);
463 p+= 4;
464
465 int4store(p, packet->max_packet_size);
466 p+= 4;
467
468 int1store(p, packet->charset_no);
469 p++;
470
471 memset(p, 0, 23); /* filler */
472 p+= 23;
473
474 if (!packet->send_half_packet) {
475 len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN);
476 memcpy(p, packet->user, len);
477 p+= len;
478 *p++ = '\0';
479
480 /* copy scrambled pass*/
481 if (packet->password && packet->password[0]) {
482 /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
483 int1store(p, SHA1_MAX_LENGTH);
484 p++;
485 php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password);
486 p+= SHA1_MAX_LENGTH;
487 } else {
488 /* Zero length */
489 int1store(p, 0);
490 p++;
491 }
492
493 if (packet->db) {
494 size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len);
495 memcpy(p, packet->db, real_db_len);
496 p+= real_db_len;
497 *p++= '\0';
498 }
499 /* Handle CLIENT_CONNECT_WITH_DB */
500 /* no \0 for no DB */
501 }
502
503 DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
504 }
505 /* }}} */
506
507
508 /* {{{ php_mysqlnd_auth_free_mem */
509 static
php_mysqlnd_auth_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)510 void php_mysqlnd_auth_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
511 {
512 if (!stack_allocation) {
513 MYSQLND_PACKET_AUTH * p = (MYSQLND_PACKET_AUTH *) _packet;
514 mnd_pefree(p, p->header.persistent);
515 }
516 }
517 /* }}} */
518
519
520 #define OK_BUFFER_SIZE 2048
521
522 /* {{{ php_mysqlnd_ok_read */
523 static enum_func_status
php_mysqlnd_ok_read(void * _packet,MYSQLND * conn TSRMLS_DC)524 php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
525 {
526 zend_uchar local_buf[OK_BUFFER_SIZE];
527 size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
528 zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
529 zend_uchar *p = buf;
530 zend_uchar *begin = buf;
531 unsigned long i;
532 register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
533
534 DBG_ENTER("php_mysqlnd_ok_read");
535
536 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
537 BAIL_IF_NO_MORE_DATA;
538
539 /* Should be always 0x0 or ERROR_MARKER for error */
540 packet->field_count = uint1korr(p);
541 p++;
542 BAIL_IF_NO_MORE_DATA;
543
544 if (ERROR_MARKER == packet->field_count) {
545 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
546 packet->error, sizeof(packet->error),
547 &packet->error_no, packet->sqlstate
548 TSRMLS_CC);
549 DBG_RETURN(PASS);
550 }
551 /* Everything was fine! */
552 packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
553 BAIL_IF_NO_MORE_DATA;
554
555 packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
556 BAIL_IF_NO_MORE_DATA;
557
558 packet->server_status = uint2korr(p);
559 p+= 2;
560 BAIL_IF_NO_MORE_DATA;
561
562 packet->warning_count = uint2korr(p);
563 p+= 2;
564 BAIL_IF_NO_MORE_DATA;
565
566 /* There is a message */
567 if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
568 packet->message_len = MIN(i, buf_len - (p - begin));
569 packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
570 } else {
571 packet->message = NULL;
572 packet->message_len = 0;
573 }
574
575 DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u",
576 packet->affected_rows, packet->last_insert_id, packet->server_status,
577 packet->warning_count);
578
579 BAIL_IF_NO_MORE_DATA;
580
581 DBG_RETURN(PASS);
582 premature_end:
583 DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
584 php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
585 p - begin - packet->header.size);
586 DBG_RETURN(FAIL);
587 }
588 /* }}} */
589
590
591 /* {{{ php_mysqlnd_ok_free_mem */
592 static void
php_mysqlnd_ok_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)593 php_mysqlnd_ok_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
594 {
595 MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet;
596 if (p->message) {
597 mnd_efree(p->message);
598 p->message = NULL;
599 }
600 if (!stack_allocation) {
601 mnd_pefree(p, p->header.persistent);
602 }
603 }
604 /* }}} */
605
606
607 /* {{{ php_mysqlnd_eof_read */
608 static enum_func_status
php_mysqlnd_eof_read(void * _packet,MYSQLND * conn TSRMLS_DC)609 php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
610 {
611 /*
612 EOF packet is since 4.1 five bytes long,
613 but we can get also an error, make it bigger.
614
615 Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
616 */
617 MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
618 size_t buf_len = conn->net->cmd_buffer.length;
619 zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
620 zend_uchar *p = buf;
621 zend_uchar *begin = buf;
622
623 DBG_ENTER("php_mysqlnd_eof_read");
624
625 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
626 BAIL_IF_NO_MORE_DATA;
627
628 /* Should be always EODATA_MARKER */
629 packet->field_count = uint1korr(p);
630 p++;
631 BAIL_IF_NO_MORE_DATA;
632
633 if (ERROR_MARKER == packet->field_count) {
634 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
635 packet->error, sizeof(packet->error),
636 &packet->error_no, packet->sqlstate
637 TSRMLS_CC);
638 DBG_RETURN(PASS);
639 }
640
641 /*
642 4.1 sends 1 byte EOF packet after metadata of
643 PREPARE/EXECUTE but 5 bytes after the result. This is not
644 according to the Docs@Forge!!!
645 */
646 if (packet->header.size > 1) {
647 packet->warning_count = uint2korr(p);
648 p+= 2;
649 BAIL_IF_NO_MORE_DATA;
650
651 packet->server_status = uint2korr(p);
652 p+= 2;
653 BAIL_IF_NO_MORE_DATA;
654 } else {
655 packet->warning_count = 0;
656 packet->server_status = 0;
657 }
658
659 BAIL_IF_NO_MORE_DATA;
660
661 DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u",
662 packet->field_count, packet->server_status, packet->warning_count);
663
664 DBG_RETURN(PASS);
665 premature_end:
666 DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
667 php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
668 p - begin - packet->header.size);
669 DBG_RETURN(FAIL);
670 }
671 /* }}} */
672
673
674 /* {{{ php_mysqlnd_eof_free_mem */
675 static
php_mysqlnd_eof_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)676 void php_mysqlnd_eof_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
677 {
678 if (!stack_allocation) {
679 mnd_pefree(_packet, ((MYSQLND_PACKET_EOF *)_packet)->header.persistent);
680 }
681 }
682 /* }}} */
683
684
685 /* {{{ php_mysqlnd_cmd_write */
php_mysqlnd_cmd_write(void * _packet,MYSQLND * conn TSRMLS_DC)686 size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
687 {
688 /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
689 MYSQLND_PACKET_COMMAND *packet= (MYSQLND_PACKET_COMMAND *) _packet;
690 MYSQLND_NET *net = conn->net;
691 unsigned int error_reporting = EG(error_reporting);
692 size_t written = 0;
693
694 DBG_ENTER("php_mysqlnd_cmd_write");
695 /*
696 Reset packet_no, or we will get bad handshake!
697 Every command starts a new TX and packet numbers are reset to 0.
698 */
699 net->packet_no = 0;
700 net->compressed_envelope_packet_no = 0; /* this is for the response */
701
702 if (error_reporting) {
703 EG(error_reporting) = 0;
704 }
705
706 MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
707
708 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
709 net->m.consume_uneaten_data(net, packet->command TSRMLS_CC);
710 #endif
711
712 if (!packet->argument || !packet->arg_len) {
713 char buffer[MYSQLND_HEADER_SIZE + 1];
714
715 int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
716 written = conn->net->m.send(conn, buffer, 1 TSRMLS_CC);
717 } else {
718 size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
719 zend_uchar *tmp, *p;
720 tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
721 if (!tmp) {
722 goto end;
723 }
724 p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
725
726 int1store(p, packet->command);
727 p++;
728
729 memcpy(p, packet->argument, packet->arg_len);
730
731 ret = conn->net->m.send(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
732 if (tmp != net->cmd_buffer.buffer) {
733 MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
734 mnd_efree(tmp);
735 }
736 written = ret;
737 }
738 end:
739 if (error_reporting) {
740 /* restore error reporting */
741 EG(error_reporting) = error_reporting;
742 }
743 DBG_RETURN(written);
744 }
745 /* }}} */
746
747
748 /* {{{ php_mysqlnd_cmd_free_mem */
749 static
php_mysqlnd_cmd_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)750 void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
751 {
752 if (!stack_allocation) {
753 MYSQLND_PACKET_COMMAND * p = (MYSQLND_PACKET_COMMAND *) _packet;
754 mnd_pefree(p, p->header.persistent);
755 }
756 }
757 /* }}} */
758
759
760 /* {{{ php_mysqlnd_rset_header_read */
761 static enum_func_status
php_mysqlnd_rset_header_read(void * _packet,MYSQLND * conn TSRMLS_DC)762 php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
763 {
764 enum_func_status ret = PASS;
765 size_t buf_len = conn->net->cmd_buffer.length;
766 zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
767 zend_uchar *p = buf;
768 zend_uchar *begin = buf;
769 size_t len;
770 MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
771
772 DBG_ENTER("php_mysqlnd_rset_header_read");
773
774 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
775 BAIL_IF_NO_MORE_DATA;
776
777 /*
778 Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
779 of encoded sequence for length.
780 */
781 if (ERROR_MARKER == *p) {
782 /* Error */
783 p++;
784 BAIL_IF_NO_MORE_DATA;
785 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
786 packet->error_info.error, sizeof(packet->error_info.error),
787 &packet->error_info.error_no, packet->error_info.sqlstate
788 TSRMLS_CC);
789 DBG_RETURN(PASS);
790 }
791
792 packet->field_count = php_mysqlnd_net_field_length(&p);
793 BAIL_IF_NO_MORE_DATA;
794
795 switch (packet->field_count) {
796 case MYSQLND_NULL_LENGTH:
797 DBG_INF("LOAD LOCAL");
798 /*
799 First byte in the packet is the field count.
800 Thus, the name is size - 1. And we add 1 for a trailing \0.
801 Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed
802 that packet->header.size is > 0. Which means that len can't underflow, that
803 would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
804 */
805 len = packet->header.size - 1;
806 packet->info_or_local_file = mnd_emalloc(len + 1);
807 if (packet->info_or_local_file) {
808 memcpy(packet->info_or_local_file, p, len);
809 packet->info_or_local_file[len] = '\0';
810 packet->info_or_local_file_len = len;
811 } else {
812 SET_OOM_ERROR(conn->error_info);
813 ret = FAIL;
814 }
815 break;
816 case 0x00:
817 DBG_INF("UPSERT");
818 packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
819 BAIL_IF_NO_MORE_DATA;
820
821 packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
822 BAIL_IF_NO_MORE_DATA;
823
824 packet->server_status = uint2korr(p);
825 p+=2;
826 BAIL_IF_NO_MORE_DATA;
827
828 packet->warning_count = uint2korr(p);
829 p+=2;
830 BAIL_IF_NO_MORE_DATA;
831 /* Check for additional textual data */
832 if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
833 packet->info_or_local_file = mnd_emalloc(len + 1);
834 if (packet->info_or_local_file) {
835 memcpy(packet->info_or_local_file, p, len);
836 packet->info_or_local_file[len] = '\0';
837 packet->info_or_local_file_len = len;
838 } else {
839 SET_OOM_ERROR(conn->error_info);
840 ret = FAIL;
841 }
842 }
843 DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%u warning_count=%u",
844 packet->affected_rows, packet->last_insert_id,
845 packet->server_status, packet->warning_count);
846 break;
847 default:
848 DBG_INF("SELECT");
849 /* Result set */
850 break;
851 }
852 BAIL_IF_NO_MORE_DATA;
853
854 DBG_RETURN(ret);
855 premature_end:
856 DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
857 php_error_docref(NULL TSRMLS_CC, E_WARNING, "RSET_HEADER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
858 p - begin - packet->header.size);
859 DBG_RETURN(FAIL);
860 }
861 /* }}} */
862
863
864 /* {{{ php_mysqlnd_rset_header_free_mem */
865 static
php_mysqlnd_rset_header_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)866 void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
867 {
868 MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
869 DBG_ENTER("php_mysqlnd_rset_header_free_mem");
870 if (p->info_or_local_file) {
871 mnd_efree(p->info_or_local_file);
872 p->info_or_local_file = NULL;
873 }
874 if (!stack_allocation) {
875 mnd_pefree(p, p->header.persistent);
876 }
877 DBG_VOID_RETURN;
878 }
879 /* }}} */
880
881 static size_t rset_field_offsets[] =
882 {
883 STRUCT_OFFSET(MYSQLND_FIELD, catalog),
884 STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
885 STRUCT_OFFSET(MYSQLND_FIELD, db),
886 STRUCT_OFFSET(MYSQLND_FIELD, db_length),
887 STRUCT_OFFSET(MYSQLND_FIELD, table),
888 STRUCT_OFFSET(MYSQLND_FIELD, table_length),
889 STRUCT_OFFSET(MYSQLND_FIELD, org_table),
890 STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
891 STRUCT_OFFSET(MYSQLND_FIELD, name),
892 STRUCT_OFFSET(MYSQLND_FIELD, name_length),
893 STRUCT_OFFSET(MYSQLND_FIELD, org_name),
894 STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
895 };
896
897
898 /* {{{ php_mysqlnd_rset_field_read */
899 static enum_func_status
php_mysqlnd_rset_field_read(void * _packet,MYSQLND * conn TSRMLS_DC)900 php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
901 {
902 /* Should be enough for the metadata of a single row */
903 MYSQLND_PACKET_RES_FIELD *packet= (MYSQLND_PACKET_RES_FIELD *) _packet;
904 size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
905 zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
906 zend_uchar *p = buf;
907 zend_uchar *begin = buf;
908 char *root_ptr;
909 unsigned long len;
910 MYSQLND_FIELD *meta;
911 unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
912
913 DBG_ENTER("php_mysqlnd_rset_field_read");
914
915 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
916
917 if (packet->skip_parsing) {
918 DBG_RETURN(PASS);
919 }
920
921 BAIL_IF_NO_MORE_DATA;
922 if (ERROR_MARKER == *p) {
923 /* Error */
924 p++;
925 BAIL_IF_NO_MORE_DATA;
926 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
927 packet->error_info.error, sizeof(packet->error_info.error),
928 &packet->error_info.error_no, packet->error_info.sqlstate
929 TSRMLS_CC);
930 DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
931 DBG_RETURN(PASS);
932 } else if (EODATA_MARKER == *p && packet->header.size < 8) {
933 /* Premature EOF. That should be COM_FIELD_LIST */
934 DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
935 packet->stupid_list_fields_eof = TRUE;
936 DBG_RETURN(PASS);
937 }
938
939 meta = packet->metadata;
940
941 for (i = 0; i < field_count; i += 2) {
942 len = php_mysqlnd_net_field_length(&p);
943 BAIL_IF_NO_MORE_DATA;
944 switch ((len)) {
945 case 0:
946 *(const char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
947 *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
948 break;
949 case MYSQLND_NULL_LENGTH:
950 goto faulty_or_fake;
951 default:
952 *(const char **)(((char *)meta) + rset_field_offsets[i]) = (const char *)p;
953 *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
954 p += len;
955 total_len += len + 1;
956 break;
957 }
958 BAIL_IF_NO_MORE_DATA;
959 }
960
961 /* 1 byte length */
962 if (12 != *p) {
963 DBG_ERR_FMT("Protocol error. Server sent false length. Expected 12 got %d", (int) *p);
964 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent false length. Expected 12");
965 }
966 p++;
967 BAIL_IF_NO_MORE_DATA;
968
969 meta->charsetnr = uint2korr(p);
970 p += 2;
971 BAIL_IF_NO_MORE_DATA;
972
973 meta->length = uint4korr(p);
974 p += 4;
975 BAIL_IF_NO_MORE_DATA;
976
977 meta->type = uint1korr(p);
978 p += 1;
979 BAIL_IF_NO_MORE_DATA;
980
981 meta->flags = uint2korr(p);
982 p += 2;
983 BAIL_IF_NO_MORE_DATA;
984
985 meta->decimals = uint1korr(p);
986 p += 1;
987 BAIL_IF_NO_MORE_DATA;
988
989 /* 2 byte filler */
990 p +=2;
991 BAIL_IF_NO_MORE_DATA;
992
993 /* Should we set NUM_FLAG (libmysql does it) ? */
994 if (
995 (meta->type <= MYSQL_TYPE_INT24 &&
996 (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
997 ) || meta->type == MYSQL_TYPE_YEAR)
998 {
999 meta->flags |= NUM_FLAG;
1000 }
1001
1002
1003 /*
1004 def could be empty, thus don't allocate on the root.
1005 NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
1006 Otherwise the string is length encoded.
1007 */
1008 if (packet->header.size > (size_t) (p - buf) &&
1009 (len = php_mysqlnd_net_field_length(&p)) &&
1010 len != MYSQLND_NULL_LENGTH)
1011 {
1012 BAIL_IF_NO_MORE_DATA;
1013 DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
1014 meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
1015 if (!meta->def) {
1016 SET_OOM_ERROR(conn->error_info);
1017 DBG_RETURN(FAIL);
1018 }
1019 memcpy(meta->def, p, len);
1020 meta->def[len] = '\0';
1021 meta->def_length = len;
1022 p += len;
1023 }
1024
1025 DBG_INF_FMT("allocing root. persistent=%u", packet->persistent_alloc);
1026 root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
1027 if (!root_ptr) {
1028 SET_OOM_ERROR(conn->error_info);
1029 DBG_RETURN(FAIL);
1030 }
1031
1032 meta->root_len = total_len;
1033 /* Now do allocs */
1034 if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
1035 len = meta->catalog_length;
1036 meta->catalog = memcpy(root_ptr, meta->catalog, len);
1037 *(root_ptr +=len) = '\0';
1038 root_ptr++;
1039 }
1040
1041 if (meta->db && meta->db != mysqlnd_empty_string) {
1042 len = meta->db_length;
1043 meta->db = memcpy(root_ptr, meta->db, len);
1044 *(root_ptr +=len) = '\0';
1045 root_ptr++;
1046 }
1047
1048 if (meta->table && meta->table != mysqlnd_empty_string) {
1049 len = meta->table_length;
1050 meta->table = memcpy(root_ptr, meta->table, len);
1051 *(root_ptr +=len) = '\0';
1052 root_ptr++;
1053 }
1054
1055 if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
1056 len = meta->org_table_length;
1057 meta->org_table = memcpy(root_ptr, meta->org_table, len);
1058 *(root_ptr +=len) = '\0';
1059 root_ptr++;
1060 }
1061
1062 if (meta->name && meta->name != mysqlnd_empty_string) {
1063 len = meta->name_length;
1064 meta->name = memcpy(root_ptr, meta->name, len);
1065 *(root_ptr +=len) = '\0';
1066 root_ptr++;
1067 }
1068
1069 if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
1070 len = meta->org_name_length;
1071 meta->org_name = memcpy(root_ptr, meta->org_name, len);
1072 *(root_ptr +=len) = '\0';
1073 root_ptr++;
1074 }
1075
1076 DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
1077 meta->name? meta->name:"*NA*");
1078
1079 DBG_RETURN(PASS);
1080
1081 faulty_or_fake:
1082 DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
1083 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
1084 " The server is faulty");
1085 DBG_RETURN(FAIL);
1086 premature_end:
1087 DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
1088 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
1089 "shorter than expected", p - begin - packet->header.size);
1090 DBG_RETURN(FAIL);
1091 }
1092 /* }}} */
1093
1094
1095 /* {{{ php_mysqlnd_rset_field_free_mem */
1096 static
php_mysqlnd_rset_field_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1097 void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
1098 {
1099 MYSQLND_PACKET_RES_FIELD *p= (MYSQLND_PACKET_RES_FIELD *) _packet;
1100 /* p->metadata was passed to us as temporal buffer */
1101 if (!stack_allocation) {
1102 mnd_pefree(p, p->header.persistent);
1103 }
1104 }
1105 /* }}} */
1106
1107
1108 /* {{{ php_mysqlnd_read_row_ex */
1109 static enum_func_status
php_mysqlnd_read_row_ex(MYSQLND * conn,MYSQLND_MEMORY_POOL * result_set_memory_pool,MYSQLND_MEMORY_POOL_CHUNK ** buffer,size_t * data_size,zend_bool persistent_alloc,unsigned int prealloc_more_bytes TSRMLS_DC)1110 php_mysqlnd_read_row_ex(MYSQLND * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
1111 MYSQLND_MEMORY_POOL_CHUNK **buffer,
1112 size_t *data_size, zend_bool persistent_alloc,
1113 unsigned int prealloc_more_bytes TSRMLS_DC)
1114 {
1115 enum_func_status ret = PASS;
1116 MYSQLND_PACKET_HEADER header;
1117 zend_uchar *p = NULL;
1118 zend_bool first_iteration = TRUE;
1119
1120 DBG_ENTER("php_mysqlnd_read_row_ex");
1121
1122 /*
1123 To ease the process the server splits everything in packets up to 2^24 - 1.
1124 Even in the case the payload is evenly divisible by this value, the last
1125 packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
1126 for next one if they have 2^24 - 1 sizes. But just read the header of a
1127 zero-length byte, don't read the body, there is no such.
1128 */
1129
1130 *data_size = prealloc_more_bytes;
1131 while (1) {
1132 if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) {
1133 ret = FAIL;
1134 break;
1135 }
1136
1137 *data_size += header.size;
1138
1139 if (first_iteration) {
1140 first_iteration = FALSE;
1141 /*
1142 We need a trailing \0 for the last string, in case of text-mode,
1143 to be able to implement read-only variables. Thus, we add + 1.
1144 */
1145 *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
1146 if (!*buffer) {
1147 ret = FAIL;
1148 break;
1149 }
1150 p = (*buffer)->ptr;
1151 } else if (!first_iteration) {
1152 /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
1153 if (!header.size) {
1154 break;
1155 }
1156
1157 /*
1158 We have to realloc the buffer.
1159
1160 We need a trailing \0 for the last string, in case of text-mode,
1161 to be able to implement read-only variables.
1162 */
1163 if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC)) {
1164 SET_OOM_ERROR(conn->error_info);
1165 ret = FAIL;
1166 break;
1167 }
1168 /* The position could have changed, recalculate */
1169 p = (*buffer)->ptr + (*data_size - header.size);
1170 }
1171
1172 if (PASS != (ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) {
1173 DBG_ERR("Empty row packet body");
1174 php_error(E_WARNING, "Empty row packet body");
1175 break;
1176 }
1177
1178 if (header.size < MYSQLND_MAX_PACKET_SIZE) {
1179 break;
1180 }
1181 }
1182 if (ret == FAIL && *buffer) {
1183 (*buffer)->free_chunk((*buffer) TSRMLS_CC);
1184 *buffer = NULL;
1185 }
1186 *data_size -= prealloc_more_bytes;
1187 DBG_RETURN(ret);
1188 }
1189 /* }}} */
1190
1191
1192 /* {{{ php_mysqlnd_rowp_read_binary_protocol */
1193 enum_func_status
php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,zval ** fields,unsigned int field_count,MYSQLND_FIELD * fields_metadata,zend_bool persistent,zend_bool as_unicode,zend_bool as_int_or_float,MYSQLND_STATS * stats TSRMLS_DC)1194 php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
1195 unsigned int field_count, MYSQLND_FIELD *fields_metadata,
1196 zend_bool persistent,
1197 zend_bool as_unicode, zend_bool as_int_or_float,
1198 MYSQLND_STATS * stats TSRMLS_DC)
1199 {
1200 unsigned int i;
1201 zend_uchar *p = row_buffer->ptr;
1202 zend_uchar *null_ptr, bit;
1203 zval **current_field, **end_field, **start_field;
1204
1205 DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
1206
1207 if (!fields) {
1208 DBG_RETURN(FAIL);
1209 }
1210
1211 end_field = (start_field = fields) + field_count;
1212
1213 /* skip the first byte, not EODATA_MARKER -> 0x0, status */
1214 p++;
1215 null_ptr= p;
1216 p += (field_count + 9)/8; /* skip null bits */
1217 bit = 4; /* first 2 bits are reserved */
1218
1219 for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1220 DBG_INF("Directly creating zval");
1221 MAKE_STD_ZVAL(*current_field);
1222 if (!*current_field) {
1223 DBG_RETURN(FAIL);
1224 }
1225 }
1226
1227 for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1228 enum_mysqlnd_collected_stats statistic;
1229 zend_uchar * orig_p = p;
1230
1231 DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u as_unicode=%u",
1232 *current_field, i,
1233 fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
1234 fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
1235 if (*null_ptr & bit) {
1236 DBG_INF("It's null");
1237 ZVAL_NULL(*current_field);
1238 statistic = STAT_BINARY_TYPE_FETCHED_NULL;
1239 } else {
1240 enum_mysqlnd_field_types type = fields_metadata[i].type;
1241 mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], 0, &p, as_unicode TSRMLS_CC);
1242
1243 if (MYSQLND_G(collect_statistics)) {
1244 switch (fields_metadata[i].type) {
1245 case MYSQL_TYPE_DECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
1246 case MYSQL_TYPE_TINY: statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
1247 case MYSQL_TYPE_SHORT: statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
1248 case MYSQL_TYPE_LONG: statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
1249 case MYSQL_TYPE_FLOAT: statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
1250 case MYSQL_TYPE_DOUBLE: statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
1251 case MYSQL_TYPE_NULL: statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
1252 case MYSQL_TYPE_TIMESTAMP: statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
1253 case MYSQL_TYPE_LONGLONG: statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
1254 case MYSQL_TYPE_INT24: statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
1255 case MYSQL_TYPE_DATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
1256 case MYSQL_TYPE_TIME: statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
1257 case MYSQL_TYPE_DATETIME: statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
1258 case MYSQL_TYPE_YEAR: statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
1259 case MYSQL_TYPE_NEWDATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
1260 case MYSQL_TYPE_VARCHAR: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1261 case MYSQL_TYPE_BIT: statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
1262 case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
1263 case MYSQL_TYPE_ENUM: statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
1264 case MYSQL_TYPE_SET: statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
1265 case MYSQL_TYPE_TINY_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1266 case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1267 case MYSQL_TYPE_LONG_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1268 case MYSQL_TYPE_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1269 case MYSQL_TYPE_VAR_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1270 case MYSQL_TYPE_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1271 case MYSQL_TYPE_GEOMETRY: statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
1272 default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
1273 }
1274 }
1275 }
1276 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
1277 STAT_BYTES_RECEIVED_PURE_DATA_PS,
1278 (Z_TYPE_PP(current_field) == IS_STRING)?
1279 Z_STRLEN_PP(current_field) : (p - orig_p));
1280
1281 if (!((bit<<=1) & 255)) {
1282 bit = 1; /* to the following byte */
1283 null_ptr++;
1284 }
1285 }
1286
1287 DBG_RETURN(PASS);
1288 }
1289 /* }}} */
1290
1291
1292 /* {{{ php_mysqlnd_rowp_read_text_protocol */
1293 enum_func_status
php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer,zval ** fields,unsigned int field_count,MYSQLND_FIELD * fields_metadata,zend_bool persistent,zend_bool as_unicode,zend_bool as_int_or_float,MYSQLND_STATS * stats TSRMLS_DC)1294 php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
1295 unsigned int field_count, MYSQLND_FIELD *fields_metadata,
1296 zend_bool persistent,
1297 zend_bool as_unicode, zend_bool as_int_or_float,
1298 MYSQLND_STATS * stats TSRMLS_DC)
1299 {
1300 unsigned int i;
1301 zend_bool last_field_was_string = FALSE;
1302 zval **current_field, **end_field, **start_field;
1303 zend_uchar *p = row_buffer->ptr;
1304 size_t data_size = row_buffer->app;
1305 zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
1306
1307 DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
1308
1309 if (!fields) {
1310 DBG_RETURN(FAIL);
1311 }
1312
1313 end_field = (start_field = fields) + field_count;
1314
1315 for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1316 DBG_INF("Directly creating zval");
1317 MAKE_STD_ZVAL(*current_field);
1318 if (!*current_field) {
1319 DBG_RETURN(FAIL);
1320 }
1321 }
1322
1323 for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1324 /* Don't reverse the order. It is significant!*/
1325 zend_uchar *this_field_len_pos = p;
1326 /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
1327 unsigned long len = php_mysqlnd_net_field_length(&p);
1328
1329 if (current_field > start_field && last_field_was_string) {
1330 /*
1331 Normal queries:
1332 We have to put \0 now to the end of the previous field, if it was
1333 a string. IS_NULL doesn't matter. Because we have already read our
1334 length, then we can overwrite it in the row buffer.
1335 This statement terminates the previous field, not the current one.
1336
1337 NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
1338 Any string's length is encoded in at least one byte, so we can stick
1339 a \0 there.
1340 */
1341
1342 *this_field_len_pos = '\0';
1343 }
1344
1345 /* NULL or NOT NULL, this is the question! */
1346 if (len == MYSQLND_NULL_LENGTH) {
1347 ZVAL_NULL(*current_field);
1348 last_field_was_string = FALSE;
1349 } else {
1350 #if MYSQLND_UNICODE || defined(MYSQLND_STRING_TO_INT_CONVERSION)
1351 struct st_mysqlnd_perm_bind perm_bind =
1352 mysqlnd_ps_fetch_functions[fields_metadata[i].type];
1353 #endif
1354 if (MYSQLND_G(collect_statistics)) {
1355 enum_mysqlnd_collected_stats statistic;
1356 switch (fields_metadata[i].type) {
1357 case MYSQL_TYPE_DECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1358 case MYSQL_TYPE_TINY: statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
1359 case MYSQL_TYPE_SHORT: statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
1360 case MYSQL_TYPE_LONG: statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
1361 case MYSQL_TYPE_FLOAT: statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
1362 case MYSQL_TYPE_DOUBLE: statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
1363 case MYSQL_TYPE_NULL: statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
1364 case MYSQL_TYPE_TIMESTAMP: statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
1365 case MYSQL_TYPE_LONGLONG: statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
1366 case MYSQL_TYPE_INT24: statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
1367 case MYSQL_TYPE_DATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1368 case MYSQL_TYPE_TIME: statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
1369 case MYSQL_TYPE_DATETIME: statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
1370 case MYSQL_TYPE_YEAR: statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
1371 case MYSQL_TYPE_NEWDATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1372 case MYSQL_TYPE_VARCHAR: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1373 case MYSQL_TYPE_BIT: statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
1374 case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1375 case MYSQL_TYPE_ENUM: statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
1376 case MYSQL_TYPE_SET: statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
1377 case MYSQL_TYPE_TINY_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1378 case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1379 case MYSQL_TYPE_LONG_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1380 case MYSQL_TYPE_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1381 case MYSQL_TYPE_VAR_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1382 case MYSQL_TYPE_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1383 case MYSQL_TYPE_GEOMETRY: statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
1384 default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
1385 }
1386 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
1387 }
1388 #ifdef MYSQLND_STRING_TO_INT_CONVERSION
1389 if (as_int_or_float && perm_bind.php_type == IS_LONG) {
1390 zend_uchar save = *(p + len);
1391 /* We have to make it ASCIIZ temporarily */
1392 *(p + len) = '\0';
1393 if (perm_bind.pack_len < SIZEOF_LONG) {
1394 /* direct conversion */
1395 int64_t v =
1396 #ifndef PHP_WIN32
1397 atoll((char *) p);
1398 #else
1399 _atoi64((char *) p);
1400 #endif
1401 ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
1402 } else {
1403 uint64_t v =
1404 #ifndef PHP_WIN32
1405 (uint64_t) atoll((char *) p);
1406 #else
1407 (uint64_t) _atoi64((char *) p);
1408 #endif
1409 zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
1410 /* We have to make it ASCIIZ temporarily */
1411 #if SIZEOF_LONG==8
1412 if (uns == TRUE && v > 9223372036854775807L)
1413 #elif SIZEOF_LONG==4
1414 if ((uns == TRUE && v > L64(2147483647)) ||
1415 (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
1416 (L64(-2147483648) > (int64_t) v))))
1417 #else
1418 #error Need fix for this architecture
1419 #endif /* SIZEOF */
1420 {
1421 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1422 } else {
1423 ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
1424 }
1425 }
1426 *(p + len) = save;
1427 } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
1428 zend_uchar save = *(p + len);
1429 /* We have to make it ASCIIZ temporarily */
1430 *(p + len) = '\0';
1431 ZVAL_DOUBLE(*current_field, atof((char *) p));
1432 *(p + len) = save;
1433 } else
1434 #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
1435 if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
1436 /*
1437 BIT fields are specially handled. As they come as bit mask, we have
1438 to convert it to human-readable representation. As the bits take
1439 less space in the protocol than the numbers they represent, we don't
1440 have enough space in the packet buffer to overwrite inside.
1441 Thus, a bit more space is pre-allocated at the end of the buffer,
1442 see php_mysqlnd_rowp_read(). And we add the strings at the end.
1443 Definitely not nice, _hackish_ :(, but works.
1444 */
1445 zend_uchar *start = bit_area;
1446 ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, as_unicode, len TSRMLS_CC);
1447 /*
1448 We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
1449 later in this function there will be an advancement.
1450 */
1451 p -= len;
1452 if (Z_TYPE_PP(current_field) == IS_LONG) {
1453 bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
1454 #if MYSQLND_UNICODE
1455 if (as_unicode) {
1456 ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
1457 } else
1458 #endif
1459 {
1460 ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
1461 }
1462 } else if (Z_TYPE_PP(current_field) == IS_STRING){
1463 memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
1464 bit_area += Z_STRLEN_PP(current_field);
1465 *bit_area++ = '\0';
1466 zval_dtor(*current_field);
1467 #if MYSQLND_UNICODE
1468 if (as_unicode) {
1469 ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
1470 } else
1471 #endif
1472 {
1473 ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
1474 }
1475 }
1476 /*
1477 IS_UNICODE should not be specially handled. In unicode mode
1478 the buffers are not referenced - everything is copied.
1479 */
1480 } else
1481 #if MYSQLND_UNICODE == 0
1482 {
1483 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1484 }
1485 #else
1486 /*
1487 Here we have to convert to UTF16, which means not reusing the buffer.
1488 Which in turn means that we can free the buffers once we have
1489 stored the result set, if we use store_result().
1490
1491 Also the destruction of the zvals should not call zval_copy_ctor()
1492 because then we will leak.
1493
1494 XXX: Keep in mind that up there there is an open `else` in
1495 #ifdef MYSQLND_STRING_TO_INT_CONVERSION
1496 which will make with this `if` an `else if`.
1497 */
1498 if ((perm_bind.is_possibly_blob == TRUE &&
1499 fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
1500 (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
1501 {
1502 /* BLOB - no conversion please */
1503 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1504 } else {
1505 ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
1506 }
1507 #endif
1508 p += len;
1509 last_field_was_string = TRUE;
1510 }
1511 }
1512 if (last_field_was_string) {
1513 /* Normal queries: The buffer has one more byte at the end, because we need it */
1514 row_buffer->ptr[data_size] = '\0';
1515 }
1516
1517 DBG_RETURN(PASS);
1518 }
1519 /* }}} */
1520
1521
1522 /* {{{ php_mysqlnd_rowp_read */
1523 /*
1524 if normal statements => packet->fields is created by this function,
1525 if PS => packet->fields is passed from outside
1526 */
1527 static enum_func_status
php_mysqlnd_rowp_read(void * _packet,MYSQLND * conn TSRMLS_DC)1528 php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1529 {
1530 MYSQLND_NET *net = conn->net;
1531 zend_uchar *p;
1532 enum_func_status ret = PASS;
1533 size_t old_chunk_size = net->stream->chunk_size;
1534 MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
1535 size_t post_alloc_for_bit_fields = 0;
1536 size_t data_size = 0;
1537
1538 DBG_ENTER("php_mysqlnd_rowp_read");
1539
1540 if (!packet->binary_protocol && packet->bit_fields_count) {
1541 /* For every field we need terminating \0 */
1542 post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
1543 }
1544
1545 ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
1546 packet->persistent_alloc, post_alloc_for_bit_fields
1547 TSRMLS_CC);
1548 if (FAIL == ret) {
1549 goto end;
1550 }
1551 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
1552 MYSQLND_HEADER_SIZE + packet->header.size,
1553 packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
1554 1);
1555
1556 /* packet->row_buffer->ptr is of size 'data_size + 1' */
1557 packet->header.size = data_size;
1558 packet->row_buffer->app = data_size;
1559
1560 if (ERROR_MARKER == (*(p = packet->row_buffer->ptr))) {
1561 /*
1562 Error message as part of the result set,
1563 not good but we should not hang. See:
1564 Bug #27876 : SF with cyrillic variable name fails during execution
1565 */
1566 ret = FAIL;
1567 php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
1568 packet->error_info.error,
1569 sizeof(packet->error_info.error),
1570 &packet->error_info.error_no,
1571 packet->error_info.sqlstate
1572 TSRMLS_CC);
1573 } else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
1574 packet->eof = TRUE;
1575 p++;
1576 if (data_size > 1) {
1577 packet->warning_count = uint2korr(p);
1578 p += 2;
1579 packet->server_status = uint2korr(p);
1580 /* Seems we have 3 bytes reserved for future use */
1581 DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
1582 }
1583 } else {
1584 MYSQLND_INC_CONN_STATISTIC(conn->stats,
1585 packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
1586 STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
1587
1588 packet->eof = FALSE;
1589 /* packet->field_count is set by the user of the packet */
1590
1591 if (!packet->skip_extraction) {
1592 if (!packet->fields) {
1593 DBG_INF("Allocating packet->fields");
1594 /*
1595 old-API will probably set packet->fields to NULL every time, though for
1596 unbuffered sets it makes not much sense as the zvals in this buffer matter,
1597 not the buffer. Constantly allocating and deallocating brings nothing.
1598
1599 For PS - if stmt_store() is performed, thus we don't have a cursor, it will
1600 behave just like old-API buffered. Cursors will behave like a bit different,
1601 but mostly like old-API unbuffered and thus will populate this array with
1602 value.
1603 */
1604 packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
1605 packet->persistent_alloc);
1606 }
1607 } else {
1608 MYSQLND_INC_CONN_STATISTIC(conn->stats,
1609 packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
1610 STAT_ROWS_SKIPPED_NORMAL);
1611 }
1612 }
1613
1614 end:
1615 net->stream->chunk_size = old_chunk_size;
1616 DBG_RETURN(ret);
1617 }
1618 /* }}} */
1619
1620
1621 /* {{{ php_mysqlnd_rowp_free_mem */
1622 static void
php_mysqlnd_rowp_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1623 php_mysqlnd_rowp_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
1624 {
1625 MYSQLND_PACKET_ROW *p;
1626
1627 DBG_ENTER("php_mysqlnd_rowp_free_mem");
1628 p = (MYSQLND_PACKET_ROW *) _packet;
1629 if (p->row_buffer) {
1630 p->row_buffer->free_chunk(p->row_buffer TSRMLS_CC);
1631 p->row_buffer = NULL;
1632 }
1633 DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
1634 /*
1635 Don't free packet->fields :
1636 - normal queries -> store_result() | fetch_row_unbuffered() will transfer
1637 the ownership and NULL it.
1638 - PS will pass in it the bound variables, we have to use them! and of course
1639 not free the array. As it is passed to us, we should not clean it ourselves.
1640 */
1641 if (!stack_allocation) {
1642 mnd_pefree(p, p->header.persistent);
1643 }
1644 DBG_VOID_RETURN;
1645 }
1646 /* }}} */
1647
1648
1649 /* {{{ php_mysqlnd_stats_read */
1650 static enum_func_status
php_mysqlnd_stats_read(void * _packet,MYSQLND * conn TSRMLS_DC)1651 php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1652 {
1653 MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
1654 size_t buf_len = conn->net->cmd_buffer.length;
1655 zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
1656
1657 DBG_ENTER("php_mysqlnd_stats_read");
1658
1659 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
1660
1661 packet->message = mnd_emalloc(packet->header.size + 1);
1662 memcpy(packet->message, buf, packet->header.size);
1663 packet->message[packet->header.size] = '\0';
1664 packet->message_len = packet->header.size;
1665
1666 DBG_RETURN(PASS);
1667 }
1668 /* }}} */
1669
1670
1671 /* {{{ php_mysqlnd_stats_free_mem */
1672 static
php_mysqlnd_stats_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1673 void php_mysqlnd_stats_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
1674 {
1675 MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
1676 if (p->message) {
1677 mnd_efree(p->message);
1678 p->message = NULL;
1679 }
1680 if (!stack_allocation) {
1681 mnd_pefree(p, p->header.persistent);
1682 }
1683 }
1684 /* }}} */
1685
1686
1687 /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
1688 #define PREPARE_RESPONSE_SIZE_41 9
1689 #define PREPARE_RESPONSE_SIZE_50 12
1690
1691 /* {{{ php_mysqlnd_prepare_read */
1692 static enum_func_status
php_mysqlnd_prepare_read(void * _packet,MYSQLND * conn TSRMLS_DC)1693 php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1694 {
1695 /* In case of an error, we should have place to put it */
1696 size_t buf_len = conn->net->cmd_buffer.length;
1697 zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
1698 zend_uchar *p = buf;
1699 zend_uchar *begin = buf;
1700 unsigned int data_size;
1701 MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
1702
1703 DBG_ENTER("php_mysqlnd_prepare_read");
1704
1705 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
1706 BAIL_IF_NO_MORE_DATA;
1707
1708 data_size = packet->header.size;
1709 packet->error_code = uint1korr(p);
1710 p++;
1711 BAIL_IF_NO_MORE_DATA;
1712
1713 if (ERROR_MARKER == packet->error_code) {
1714 php_mysqlnd_read_error_from_line(p, data_size - 1,
1715 packet->error_info.error,
1716 sizeof(packet->error_info.error),
1717 &packet->error_info.error_no,
1718 packet->error_info.sqlstate
1719 TSRMLS_CC);
1720 DBG_RETURN(PASS);
1721 }
1722
1723 if (data_size != PREPARE_RESPONSE_SIZE_41 &&
1724 data_size != PREPARE_RESPONSE_SIZE_50 &&
1725 !(data_size > PREPARE_RESPONSE_SIZE_50)) {
1726 DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size);
1727 php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size);
1728 DBG_RETURN(FAIL);
1729 }
1730
1731 packet->stmt_id = uint4korr(p);
1732 p += 4;
1733 BAIL_IF_NO_MORE_DATA;
1734
1735 /* Number of columns in result set */
1736 packet->field_count = uint2korr(p);
1737 p += 2;
1738 BAIL_IF_NO_MORE_DATA;
1739
1740 packet->param_count = uint2korr(p);
1741 p += 2;
1742 BAIL_IF_NO_MORE_DATA;
1743
1744 if (data_size > 9) {
1745 /* 0x0 filler sent by the server for 5.0+ clients */
1746 p++;
1747 BAIL_IF_NO_MORE_DATA;
1748
1749 packet->warning_count = uint2korr(p);
1750 }
1751
1752 DBG_INF_FMT("Prepare packet read: stmt_id=%u fields=%u params=%u",
1753 packet->stmt_id, packet->field_count, packet->param_count);
1754
1755 BAIL_IF_NO_MORE_DATA;
1756
1757 DBG_RETURN(PASS);
1758 premature_end:
1759 DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
1760 php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
1761 p - begin - packet->header.size);
1762 DBG_RETURN(FAIL);
1763 }
1764 /* }}} */
1765
1766
1767 /* {{{ php_mysqlnd_prepare_free_mem */
1768 static void
php_mysqlnd_prepare_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1769 php_mysqlnd_prepare_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
1770 {
1771 MYSQLND_PACKET_PREPARE_RESPONSE *p= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
1772 if (!stack_allocation) {
1773 mnd_pefree(p, p->header.persistent);
1774 }
1775 }
1776 /* }}} */
1777
1778
1779 /* {{{ php_mysqlnd_chg_user_read */
1780 static enum_func_status
php_mysqlnd_chg_user_read(void * _packet,MYSQLND * conn TSRMLS_DC)1781 php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1782 {
1783 /* There could be an error message */
1784 size_t buf_len = conn->net->cmd_buffer.length;
1785 zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
1786 zend_uchar *p = buf;
1787 zend_uchar *begin = buf;
1788 MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
1789
1790 DBG_ENTER("php_mysqlnd_chg_user_read");
1791
1792 PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
1793 BAIL_IF_NO_MORE_DATA;
1794
1795 /*
1796 Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
1797 of encoded sequence for length.
1798 */
1799
1800 /* Should be always 0x0 or ERROR_MARKER for error */
1801 packet->field_count= uint1korr(p);
1802 p++;
1803
1804 if (packet->header.size == 1 && buf[0] == EODATA_MARKER && packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
1805 /* We don't handle 3.23 authentication */
1806 packet->server_asked_323_auth = TRUE;
1807 DBG_RETURN(FAIL);
1808 }
1809
1810 if (ERROR_MARKER == packet->field_count) {
1811 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1812 packet->error_info.error,
1813 sizeof(packet->error_info.error),
1814 &packet->error_info.error_no,
1815 packet->error_info.sqlstate
1816 TSRMLS_CC);
1817 }
1818 BAIL_IF_NO_MORE_DATA;
1819
1820 DBG_RETURN(PASS);
1821 premature_end:
1822 DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
1823 php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
1824 p - begin - packet->header.size);
1825 DBG_RETURN(FAIL);
1826 }
1827 /* }}} */
1828
1829
1830 /* {{{ php_mysqlnd_chg_user_free_mem */
1831 static void
php_mysqlnd_chg_user_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1832 php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
1833 {
1834 if (!stack_allocation) {
1835 mnd_pefree(_packet, ((MYSQLND_PACKET_CHG_USER_RESPONSE *)_packet)->header.persistent);
1836 }
1837 }
1838 /* }}} */
1839
1840
1841 /* {{{ packet_methods */
1842 static
1843 mysqlnd_packet_methods packet_methods[PROT_LAST] =
1844 {
1845 {
1846 sizeof(MYSQLND_PACKET_GREET),
1847 php_mysqlnd_greet_read,
1848 NULL, /* write */
1849 php_mysqlnd_greet_free_mem,
1850 }, /* PROT_GREET_PACKET */
1851 {
1852 sizeof(MYSQLND_PACKET_AUTH),
1853 NULL, /* read */
1854 php_mysqlnd_auth_write,
1855 php_mysqlnd_auth_free_mem,
1856 }, /* PROT_AUTH_PACKET */
1857 {
1858 sizeof(MYSQLND_PACKET_OK),
1859 php_mysqlnd_ok_read, /* read */
1860 NULL, /* write */
1861 php_mysqlnd_ok_free_mem,
1862 }, /* PROT_OK_PACKET */
1863 {
1864 sizeof(MYSQLND_PACKET_EOF),
1865 php_mysqlnd_eof_read, /* read */
1866 NULL, /* write */
1867 php_mysqlnd_eof_free_mem,
1868 }, /* PROT_EOF_PACKET */
1869 {
1870 sizeof(MYSQLND_PACKET_COMMAND),
1871 NULL, /* read */
1872 php_mysqlnd_cmd_write, /* write */
1873 php_mysqlnd_cmd_free_mem,
1874 }, /* PROT_CMD_PACKET */
1875 {
1876 sizeof(MYSQLND_PACKET_RSET_HEADER),
1877 php_mysqlnd_rset_header_read, /* read */
1878 NULL, /* write */
1879 php_mysqlnd_rset_header_free_mem,
1880 }, /* PROT_RSET_HEADER_PACKET */
1881 {
1882 sizeof(MYSQLND_PACKET_RES_FIELD),
1883 php_mysqlnd_rset_field_read, /* read */
1884 NULL, /* write */
1885 php_mysqlnd_rset_field_free_mem,
1886 }, /* PROT_RSET_FLD_PACKET */
1887 {
1888 sizeof(MYSQLND_PACKET_ROW),
1889 php_mysqlnd_rowp_read, /* read */
1890 NULL, /* write */
1891 php_mysqlnd_rowp_free_mem,
1892 }, /* PROT_ROW_PACKET */
1893 {
1894 sizeof(MYSQLND_PACKET_STATS),
1895 php_mysqlnd_stats_read, /* read */
1896 NULL, /* write */
1897 php_mysqlnd_stats_free_mem,
1898 }, /* PROT_STATS_PACKET */
1899 {
1900 sizeof(MYSQLND_PACKET_PREPARE_RESPONSE),
1901 php_mysqlnd_prepare_read, /* read */
1902 NULL, /* write */
1903 php_mysqlnd_prepare_free_mem,
1904 }, /* PROT_PREPARE_RESP_PACKET */
1905 {
1906 sizeof(MYSQLND_PACKET_CHG_USER_RESPONSE),
1907 php_mysqlnd_chg_user_read, /* read */
1908 NULL, /* write */
1909 php_mysqlnd_chg_user_free_mem,
1910 } /* PROT_CHG_USER_RESP_PACKET */
1911 };
1912 /* }}} */
1913
1914
1915 /* {{{ mysqlnd_protocol::get_greet_packet */
1916 static struct st_mysqlnd_packet_greet *
MYSQLND_METHOD(mysqlnd_protocol,get_greet_packet)1917 MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
1918 {
1919 struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
1920 DBG_ENTER("mysqlnd_protocol::get_greet_packet");
1921 if (packet) {
1922 packet->header.m = &packet_methods[PROT_GREET_PACKET];
1923 packet->header.persistent = persistent;
1924 }
1925 DBG_RETURN(packet);
1926 }
1927 /* }}} */
1928
1929
1930 /* {{{ mysqlnd_protocol::get_auth_packet */
1931 static struct st_mysqlnd_packet_auth *
MYSQLND_METHOD(mysqlnd_protocol,get_auth_packet)1932 MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
1933 {
1934 struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
1935 DBG_ENTER("mysqlnd_protocol::get_auth_packet");
1936 if (packet) {
1937 packet->header.m = &packet_methods[PROT_AUTH_PACKET];
1938 packet->header.persistent = persistent;
1939 }
1940 DBG_RETURN(packet);
1941 }
1942 /* }}} */
1943
1944
1945 /* {{{ mysqlnd_protocol::get_ok_packet */
1946 static struct st_mysqlnd_packet_ok *
MYSQLND_METHOD(mysqlnd_protocol,get_ok_packet)1947 MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
1948 {
1949 struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
1950 DBG_ENTER("mysqlnd_protocol::get_ok_packet");
1951 if (packet) {
1952 packet->header.m = &packet_methods[PROT_OK_PACKET];
1953 packet->header.persistent = persistent;
1954 }
1955 DBG_RETURN(packet);
1956 }
1957 /* }}} */
1958
1959
1960 /* {{{ mysqlnd_protocol::get_eof_packet */
1961 static struct st_mysqlnd_packet_eof *
MYSQLND_METHOD(mysqlnd_protocol,get_eof_packet)1962 MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
1963 {
1964 struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
1965 DBG_ENTER("mysqlnd_protocol::get_eof_packet");
1966 if (packet) {
1967 packet->header.m = &packet_methods[PROT_EOF_PACKET];
1968 packet->header.persistent = persistent;
1969 }
1970 DBG_RETURN(packet);
1971 }
1972 /* }}} */
1973
1974
1975 /* {{{ mysqlnd_protocol::get_command_packet */
1976 static struct st_mysqlnd_packet_command *
MYSQLND_METHOD(mysqlnd_protocol,get_command_packet)1977 MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
1978 {
1979 struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
1980 DBG_ENTER("mysqlnd_protocol::get_command_packet");
1981 if (packet) {
1982 packet->header.m = &packet_methods[PROT_CMD_PACKET];
1983 packet->header.persistent = persistent;
1984 }
1985 DBG_RETURN(packet);
1986 }
1987 /* }}} */
1988
1989
1990 /* {{{ mysqlnd_protocol::get_rset_packet */
1991 static struct st_mysqlnd_packet_rset_header *
MYSQLND_METHOD(mysqlnd_protocol,get_rset_header_packet)1992 MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
1993 {
1994 struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
1995 DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
1996 if (packet) {
1997 packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
1998 packet->header.persistent = persistent;
1999 }
2000 DBG_RETURN(packet);
2001 }
2002 /* }}} */
2003
2004
2005 /* {{{ mysqlnd_protocol::get_result_field_packet */
2006 static struct st_mysqlnd_packet_res_field *
MYSQLND_METHOD(mysqlnd_protocol,get_result_field_packet)2007 MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2008 {
2009 struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
2010 DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
2011 if (packet) {
2012 packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
2013 packet->header.persistent = persistent;
2014 }
2015 DBG_RETURN(packet);
2016 }
2017 /* }}} */
2018
2019
2020 /* {{{ mysqlnd_protocol::get_row_packet */
2021 static struct st_mysqlnd_packet_row *
MYSQLND_METHOD(mysqlnd_protocol,get_row_packet)2022 MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2023 {
2024 struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
2025 DBG_ENTER("mysqlnd_protocol::get_row_packet");
2026 if (packet) {
2027 packet->header.m = &packet_methods[PROT_ROW_PACKET];
2028 packet->header.persistent = persistent;
2029 }
2030 DBG_RETURN(packet);
2031 }
2032 /* }}} */
2033
2034
2035 /* {{{ mysqlnd_protocol::get_stats_packet */
2036 static struct st_mysqlnd_packet_stats *
MYSQLND_METHOD(mysqlnd_protocol,get_stats_packet)2037 MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2038 {
2039 struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
2040 DBG_ENTER("mysqlnd_protocol::get_stats_packet");
2041 if (packet) {
2042 packet->header.m = &packet_methods[PROT_STATS_PACKET];
2043 packet->header.persistent = persistent;
2044 }
2045 DBG_RETURN(packet);
2046 }
2047 /* }}} */
2048
2049
2050 /* {{{ mysqlnd_protocol::get_prepare_response_packet */
2051 static struct st_mysqlnd_packet_prepare_response *
MYSQLND_METHOD(mysqlnd_protocol,get_prepare_response_packet)2052 MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2053 {
2054 struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
2055 DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
2056 if (packet) {
2057 packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
2058 packet->header.persistent = persistent;
2059 }
2060 DBG_RETURN(packet);
2061 }
2062 /* }}} */
2063
2064
2065 /* {{{ mysqlnd_protocol::get_change_user_response_packet */
2066 static struct st_mysqlnd_packet_chg_user_resp*
MYSQLND_METHOD(mysqlnd_protocol,get_change_user_response_packet)2067 MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2068 {
2069 struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
2070 DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
2071 if (packet) {
2072 packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
2073 packet->header.persistent = persistent;
2074 }
2075 DBG_RETURN(packet);
2076 }
2077 /* }}} */
2078
2079
2080 static
2081 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
2082 MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
2083 MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
2084 MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet),
2085 MYSQLND_METHOD(mysqlnd_protocol, get_command_packet),
2086 MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet),
2087 MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet),
2088 MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet),
2089 MYSQLND_METHOD(mysqlnd_protocol, get_row_packet),
2090 MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet),
2091 MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
2092 MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)
2093 MYSQLND_CLASS_METHODS_END;
2094
2095
2096 /* {{{ mysqlnd_protocol_init */
2097 PHPAPI MYSQLND_PROTOCOL *
mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)2098 mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)
2099 {
2100 size_t alloc_size = sizeof(MYSQLND_PROTOCOL) + mysqlnd_plugin_count() * sizeof(void *);
2101 MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent);
2102
2103 DBG_ENTER("mysqlnd_protocol_init");
2104 DBG_INF_FMT("persistent=%u", persistent);
2105 if (ret) {
2106 ret->persistent = persistent;
2107 ret->m = mysqlnd_mysqlnd_protocol_methods;
2108 }
2109
2110 DBG_RETURN(ret);
2111 }
2112 /* }}} */
2113
2114
2115 /* {{{ mysqlnd_protocol_free */
2116 PHPAPI void
mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)2117 mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)
2118 {
2119 DBG_ENTER("mysqlnd_protocol_free");
2120
2121 if (protocol) {
2122 zend_bool pers = protocol->persistent;
2123 mnd_pefree(protocol, pers);
2124 }
2125 DBG_VOID_RETURN;
2126 }
2127 /* }}} */
2128
2129
2130 /* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
2131 PHPAPI void **
_mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol,unsigned int plugin_id TSRMLS_DC)2132 _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC)
2133 {
2134 DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data");
2135 DBG_INF_FMT("plugin_id=%u", plugin_id);
2136 if (!protocol || plugin_id >= mysqlnd_plugin_count()) {
2137 return NULL;
2138 }
2139 DBG_RETURN((void *)((char *)protocol + sizeof(MYSQLND_PROTOCOL) + plugin_id * sizeof(void *)));
2140 }
2141 /* }}} */
2142
2143
2144 /* {{{ mysqlnd_protocol_get_methods */
2145 PHPAPI struct st_mysqlnd_protocol_methods *
mysqlnd_protocol_get_methods()2146 mysqlnd_protocol_get_methods()
2147 {
2148 return &mysqlnd_mysqlnd_protocol_methods;
2149 }
2150 /* }}} */
2151
2152
2153 /*
2154 * Local variables:
2155 * tab-width: 4
2156 * c-basic-offset: 4
2157 * End:
2158 * vim600: noet sw=4 ts=4 fdm=marker
2159 * vim<600: noet sw=4 ts=4
2160 */
2161