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