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