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