1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2015 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 
1576 /* {{{ php_mysqlnd_rowp_read_text_protocol */
1577 enum_func_status
php_mysqlnd_rowp_read_text_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)1578 php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
1579 									unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
1580 									zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC)
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 
1589 	DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
1590 
1591 	if (!fields) {
1592 		DBG_RETURN(FAIL);
1593 	}
1594 
1595 	end_field = (start_field = fields) + field_count;
1596 
1597 	for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1598 		DBG_INF("Directly creating zval");
1599 		MAKE_STD_ZVAL(*current_field);
1600 		if (!*current_field) {
1601 			DBG_RETURN(FAIL);
1602 		}
1603 	}
1604 
1605 	for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
1606 		/* Don't reverse the order. It is significant!*/
1607 		zend_uchar *this_field_len_pos = p;
1608 		/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
1609 		unsigned long len = php_mysqlnd_net_field_length(&p);
1610 
1611 		if (current_field > start_field && last_field_was_string) {
1612 			/*
1613 			  Normal queries:
1614 			  We have to put \0 now to the end of the previous field, if it was
1615 			  a string. IS_NULL doesn't matter. Because we have already read our
1616 			  length, then we can overwrite it in the row buffer.
1617 			  This statement terminates the previous field, not the current one.
1618 
1619 			  NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
1620 			  Any string's length is encoded in at least one byte, so we can stick
1621 			  a \0 there.
1622 			*/
1623 
1624 			*this_field_len_pos = '\0';
1625 		}
1626 
1627 		/* NULL or NOT NULL, this is the question! */
1628 		if (len == MYSQLND_NULL_LENGTH) {
1629 			ZVAL_NULL(*current_field);
1630 			last_field_was_string = FALSE;
1631 		} else {
1632 #if defined(MYSQLND_STRING_TO_INT_CONVERSION)
1633 			struct st_mysqlnd_perm_bind perm_bind =
1634 					mysqlnd_ps_fetch_functions[fields_metadata[i].type];
1635 #endif
1636 			if (MYSQLND_G(collect_statistics)) {
1637 				enum_mysqlnd_collected_stats statistic;
1638 				switch (fields_metadata[i].type) {
1639 					case MYSQL_TYPE_DECIMAL:	statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1640 					case MYSQL_TYPE_TINY:		statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
1641 					case MYSQL_TYPE_SHORT:		statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
1642 					case MYSQL_TYPE_LONG:		statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
1643 					case MYSQL_TYPE_FLOAT:		statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
1644 					case MYSQL_TYPE_DOUBLE:		statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
1645 					case MYSQL_TYPE_NULL:		statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
1646 					case MYSQL_TYPE_TIMESTAMP:	statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
1647 					case MYSQL_TYPE_LONGLONG:	statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
1648 					case MYSQL_TYPE_INT24:		statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
1649 					case MYSQL_TYPE_DATE:		statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1650 					case MYSQL_TYPE_TIME:		statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
1651 					case MYSQL_TYPE_DATETIME:	statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
1652 					case MYSQL_TYPE_YEAR:		statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
1653 					case MYSQL_TYPE_NEWDATE:	statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1654 					case MYSQL_TYPE_VARCHAR:	statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1655 					case MYSQL_TYPE_BIT:		statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
1656 					case MYSQL_TYPE_NEWDECIMAL:	statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1657 					case MYSQL_TYPE_ENUM:		statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
1658 					case MYSQL_TYPE_SET:		statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
1659 					case MYSQL_TYPE_TINY_BLOB:	statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1660 					case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1661 					case MYSQL_TYPE_LONG_BLOB:	statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1662 					case MYSQL_TYPE_BLOB:		statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1663 					case MYSQL_TYPE_VAR_STRING:	statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1664 					case MYSQL_TYPE_STRING:		statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1665 					case MYSQL_TYPE_GEOMETRY:	statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
1666 					default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
1667 				}
1668 				MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
1669 			}
1670 #ifdef MYSQLND_STRING_TO_INT_CONVERSION
1671 			if (as_int_or_float && perm_bind.php_type == IS_LONG) {
1672 				zend_uchar save = *(p + len);
1673 				/* We have to make it ASCIIZ temporarily */
1674 				*(p + len) = '\0';
1675 				if (perm_bind.pack_len < SIZEOF_LONG) {
1676 					/* direct conversion */
1677 					int64_t v =
1678 #ifndef PHP_WIN32
1679 						atoll((char *) p);
1680 #else
1681 						_atoi64((char *) p);
1682 #endif
1683 					ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
1684 				} else {
1685 					uint64_t v =
1686 #ifndef PHP_WIN32
1687 						(uint64_t) atoll((char *) p);
1688 #else
1689 						(uint64_t) _atoi64((char *) p);
1690 #endif
1691 					zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
1692 					/* We have to make it ASCIIZ temporarily */
1693 #if SIZEOF_LONG==8
1694 					if (uns == TRUE && v > 9223372036854775807L)
1695 #elif SIZEOF_LONG==4
1696 					if ((uns == TRUE && v > L64(2147483647)) ||
1697 						(uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
1698 						(L64(-2147483648) > (int64_t) v))))
1699 #else
1700 #error Need fix for this architecture
1701 #endif /* SIZEOF */
1702 					{
1703 						ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1704 					} else {
1705 						ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
1706 					}
1707 				}
1708 				*(p + len) = save;
1709 			} else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
1710 				zend_uchar save = *(p + len);
1711 				/* We have to make it ASCIIZ temporarily */
1712 				*(p + len) = '\0';
1713 				ZVAL_DOUBLE(*current_field, atof((char *) p));
1714 				*(p + len) = save;
1715 			} else
1716 #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
1717 			if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
1718 				/*
1719 				  BIT fields are specially handled. As they come as bit mask, we have
1720 				  to convert it to human-readable representation. As the bits take
1721 				  less space in the protocol than the numbers they represent, we don't
1722 				  have enough space in the packet buffer to overwrite inside.
1723 				  Thus, a bit more space is pre-allocated at the end of the buffer,
1724 				  see php_mysqlnd_rowp_read(). And we add the strings at the end.
1725 				  Definitely not nice, _hackish_ :(, but works.
1726 				*/
1727 				zend_uchar *start = bit_area;
1728 				ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, len TSRMLS_CC);
1729 				/*
1730 				  We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
1731 				  later in this function there will be an advancement.
1732 				*/
1733 				p -= len;
1734 				if (Z_TYPE_PP(current_field) == IS_LONG) {
1735 					bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
1736 					ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
1737 				} else if (Z_TYPE_PP(current_field) == IS_STRING){
1738 					memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
1739 					bit_area += Z_STRLEN_PP(current_field);
1740 					*bit_area++ = '\0';
1741 					zval_dtor(*current_field);
1742 					ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
1743 				}
1744 			} else {
1745 				ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1746 			}
1747 			p += len;
1748 			last_field_was_string = TRUE;
1749 		}
1750 	}
1751 	if (last_field_was_string) {
1752 		/* Normal queries: The buffer has one more byte at the end, because we need it */
1753 		row_buffer->ptr[data_size] = '\0';
1754 	}
1755 
1756 	DBG_RETURN(PASS);
1757 }
1758 /* }}} */
1759 
1760 
1761 /* {{{ php_mysqlnd_rowp_read */
1762 /*
1763   if normal statements => packet->fields is created by this function,
1764   if PS => packet->fields is passed from outside
1765 */
1766 static enum_func_status
php_mysqlnd_rowp_read(void * _packet,MYSQLND_CONN_DATA * conn TSRMLS_DC)1767 php_mysqlnd_rowp_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
1768 {
1769 	zend_uchar *p;
1770 	enum_func_status ret = PASS;
1771 	MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
1772 	size_t post_alloc_for_bit_fields = 0;
1773 	size_t data_size = 0;
1774 
1775 	DBG_ENTER("php_mysqlnd_rowp_read");
1776 
1777 	if (!packet->binary_protocol && packet->bit_fields_count) {
1778 		/* For every field we need terminating \0 */
1779 		post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
1780 	}
1781 
1782 	ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
1783 								  packet->persistent_alloc, post_alloc_for_bit_fields
1784 								  TSRMLS_CC);
1785 	if (FAIL == ret) {
1786 		goto end;
1787 	}
1788 	MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
1789 										MYSQLND_HEADER_SIZE + packet->header.size,
1790 										packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
1791 										1);
1792 
1793 	/* packet->row_buffer->ptr is of size 'data_size + 1' */
1794 	packet->header.size = data_size;
1795 	packet->row_buffer->app = data_size;
1796 
1797 	if (ERROR_MARKER == (*(p = packet->row_buffer->ptr))) {
1798 		/*
1799 		   Error message as part of the result set,
1800 		   not good but we should not hang. See:
1801 		   Bug #27876 : SF with cyrillic variable name fails during execution
1802 		*/
1803 		ret = FAIL;
1804 		php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
1805 										 packet->error_info.error,
1806 										 sizeof(packet->error_info.error),
1807 										 &packet->error_info.error_no,
1808 										 packet->error_info.sqlstate
1809 										 TSRMLS_CC);
1810 	} else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
1811 		packet->eof = TRUE;
1812 		p++;
1813 		if (data_size > 1) {
1814 			packet->warning_count = uint2korr(p);
1815 			p += 2;
1816 			packet->server_status = uint2korr(p);
1817 			/* Seems we have 3 bytes reserved for future use */
1818 			DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
1819 		}
1820 	} else {
1821 		MYSQLND_INC_CONN_STATISTIC(conn->stats,
1822 									packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
1823 															 STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
1824 
1825 		packet->eof = FALSE;
1826 		/* packet->field_count is set by the user of the packet */
1827 
1828 		if (!packet->skip_extraction) {
1829 			if (!packet->fields) {
1830 				DBG_INF("Allocating packet->fields");
1831 				/*
1832 				  old-API will probably set packet->fields to NULL every time, though for
1833 				  unbuffered sets it makes not much sense as the zvals in this buffer matter,
1834 				  not the buffer. Constantly allocating and deallocating brings nothing.
1835 
1836 				  For PS - if stmt_store() is performed, thus we don't have a cursor, it will
1837 				  behave just like old-API buffered. Cursors will behave like a bit different,
1838 				  but mostly like old-API unbuffered and thus will populate this array with
1839 				  value.
1840 				*/
1841 				packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
1842 														packet->persistent_alloc);
1843 			}
1844 		} else {
1845 			MYSQLND_INC_CONN_STATISTIC(conn->stats,
1846 										packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
1847 																 STAT_ROWS_SKIPPED_NORMAL);
1848 		}
1849 	}
1850 
1851 end:
1852 	DBG_RETURN(ret);
1853 }
1854 /* }}} */
1855 
1856 
1857 /* {{{ php_mysqlnd_rowp_free_mem */
1858 static void
php_mysqlnd_rowp_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1859 php_mysqlnd_rowp_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
1860 {
1861 	MYSQLND_PACKET_ROW *p;
1862 
1863 	DBG_ENTER("php_mysqlnd_rowp_free_mem");
1864 	p = (MYSQLND_PACKET_ROW *) _packet;
1865 	if (p->row_buffer) {
1866 		p->row_buffer->free_chunk(p->row_buffer TSRMLS_CC);
1867 		p->row_buffer = NULL;
1868 	}
1869 	DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
1870 	/*
1871 	  Don't free packet->fields :
1872 	  - normal queries -> store_result() | fetch_row_unbuffered() will transfer
1873 	    the ownership and NULL it.
1874 	  - PS will pass in it the bound variables, we have to use them! and of course
1875 	    not free the array. As it is passed to us, we should not clean it ourselves.
1876 	*/
1877 	if (!stack_allocation) {
1878 		mnd_pefree(p, p->header.persistent);
1879 	}
1880 	DBG_VOID_RETURN;
1881 }
1882 /* }}} */
1883 
1884 
1885 /* {{{ php_mysqlnd_stats_read */
1886 static enum_func_status
php_mysqlnd_stats_read(void * _packet,MYSQLND_CONN_DATA * conn TSRMLS_DC)1887 php_mysqlnd_stats_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
1888 {
1889 	MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
1890 	size_t buf_len = conn->net->cmd_buffer.length;
1891 	zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
1892 
1893 	DBG_ENTER("php_mysqlnd_stats_read");
1894 
1895 	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
1896 
1897 	packet->message = mnd_emalloc(packet->header.size + 1);
1898 	memcpy(packet->message, buf, packet->header.size);
1899 	packet->message[packet->header.size] = '\0';
1900 	packet->message_len = packet->header.size;
1901 
1902 	DBG_RETURN(PASS);
1903 }
1904 /* }}} */
1905 
1906 
1907 /* {{{ php_mysqlnd_stats_free_mem */
1908 static
php_mysqlnd_stats_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)1909 void php_mysqlnd_stats_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
1910 {
1911 	MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
1912 	if (p->message) {
1913 		mnd_efree(p->message);
1914 		p->message = NULL;
1915 	}
1916 	if (!stack_allocation) {
1917 		mnd_pefree(p, p->header.persistent);
1918 	}
1919 }
1920 /* }}} */
1921 
1922 
1923 /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
1924 #define PREPARE_RESPONSE_SIZE_41 9
1925 #define PREPARE_RESPONSE_SIZE_50 12
1926 
1927 /* {{{ php_mysqlnd_prepare_read */
1928 static enum_func_status
php_mysqlnd_prepare_read(void * _packet,MYSQLND_CONN_DATA * conn TSRMLS_DC)1929 php_mysqlnd_prepare_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
1930 {
1931 	/* In case of an error, we should have place to put it */
1932 	size_t buf_len = conn->net->cmd_buffer.length;
1933 	zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
1934 	zend_uchar *p = buf;
1935 	zend_uchar *begin = buf;
1936 	unsigned int data_size;
1937 	MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
1938 
1939 	DBG_ENTER("php_mysqlnd_prepare_read");
1940 
1941 	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
1942 	BAIL_IF_NO_MORE_DATA;
1943 
1944 	data_size = packet->header.size;
1945 	packet->error_code = uint1korr(p);
1946 	p++;
1947 	BAIL_IF_NO_MORE_DATA;
1948 
1949 	if (ERROR_MARKER == packet->error_code) {
1950 		php_mysqlnd_read_error_from_line(p, data_size - 1,
1951 										 packet->error_info.error,
1952 										 sizeof(packet->error_info.error),
1953 										 &packet->error_info.error_no,
1954 										 packet->error_info.sqlstate
1955 										 TSRMLS_CC);
1956 		DBG_RETURN(PASS);
1957 	}
1958 
1959 	if (data_size != PREPARE_RESPONSE_SIZE_41 &&
1960 		data_size != PREPARE_RESPONSE_SIZE_50 &&
1961 		!(data_size > PREPARE_RESPONSE_SIZE_50)) {
1962 		DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size);
1963 		php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size);
1964 		DBG_RETURN(FAIL);
1965 	}
1966 
1967 	packet->stmt_id = uint4korr(p);
1968 	p += 4;
1969 	BAIL_IF_NO_MORE_DATA;
1970 
1971 	/* Number of columns in result set */
1972 	packet->field_count = uint2korr(p);
1973 	p += 2;
1974 	BAIL_IF_NO_MORE_DATA;
1975 
1976 	packet->param_count = uint2korr(p);
1977 	p += 2;
1978 	BAIL_IF_NO_MORE_DATA;
1979 
1980 	if (data_size > 9) {
1981 		/* 0x0 filler sent by the server for 5.0+ clients */
1982 		p++;
1983 		BAIL_IF_NO_MORE_DATA;
1984 
1985 		packet->warning_count = uint2korr(p);
1986 	}
1987 
1988 	DBG_INF_FMT("Prepare packet read: stmt_id=%u fields=%u params=%u",
1989 				packet->stmt_id, packet->field_count, packet->param_count);
1990 
1991 	BAIL_IF_NO_MORE_DATA;
1992 
1993 	DBG_RETURN(PASS);
1994 premature_end:
1995 	DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
1996 	php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
1997 					 p - begin - packet->header.size);
1998 	DBG_RETURN(FAIL);
1999 }
2000 /* }}} */
2001 
2002 
2003 /* {{{ php_mysqlnd_prepare_free_mem */
2004 static void
php_mysqlnd_prepare_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)2005 php_mysqlnd_prepare_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
2006 {
2007 	MYSQLND_PACKET_PREPARE_RESPONSE *p= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
2008 	if (!stack_allocation) {
2009 		mnd_pefree(p, p->header.persistent);
2010 	}
2011 }
2012 /* }}} */
2013 
2014 
2015 /* {{{ php_mysqlnd_chg_user_read */
2016 static enum_func_status
php_mysqlnd_chg_user_read(void * _packet,MYSQLND_CONN_DATA * conn TSRMLS_DC)2017 php_mysqlnd_chg_user_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
2018 {
2019 	/* There could be an error message */
2020 	size_t buf_len = conn->net->cmd_buffer.length;
2021 	zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
2022 	zend_uchar *p = buf;
2023 	zend_uchar *begin = buf;
2024 	MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
2025 
2026 	DBG_ENTER("php_mysqlnd_chg_user_read");
2027 
2028 	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
2029 	BAIL_IF_NO_MORE_DATA;
2030 
2031 	/*
2032 	  Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
2033 	  of encoded sequence for length.
2034 	*/
2035 
2036 	/* Should be always 0x0 or ERROR_MARKER for error */
2037 	packet->response_code = uint1korr(p);
2038 	p++;
2039 
2040 	if (packet->header.size == 1 && buf[0] == EODATA_MARKER && packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
2041 		/* We don't handle 3.23 authentication */
2042 		packet->server_asked_323_auth = TRUE;
2043 		DBG_RETURN(FAIL);
2044 	}
2045 
2046 	if (ERROR_MARKER == packet->response_code) {
2047 		php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
2048 										 packet->error_info.error,
2049 										 sizeof(packet->error_info.error),
2050 										 &packet->error_info.error_no,
2051 										 packet->error_info.sqlstate
2052 										 TSRMLS_CC);
2053 	}
2054 	BAIL_IF_NO_MORE_DATA;
2055 	if (packet->response_code == 0xFE && packet->header.size > (size_t) (p - buf)) {
2056 		packet->new_auth_protocol = mnd_pestrdup((char *)p, FALSE);
2057 		packet->new_auth_protocol_len = strlen(packet->new_auth_protocol);
2058 		p+= packet->new_auth_protocol_len + 1; /* +1 for the \0 */
2059 		packet->new_auth_protocol_data_len = packet->header.size - (size_t) (p - buf);
2060 		if (packet->new_auth_protocol_data_len) {
2061 			packet->new_auth_protocol_data = mnd_emalloc(packet->new_auth_protocol_data_len);
2062 			memcpy(packet->new_auth_protocol_data, p, packet->new_auth_protocol_data_len);
2063 		}
2064 		DBG_INF_FMT("The server requested switching auth plugin to : %s", packet->new_auth_protocol);
2065 		DBG_INF_FMT("Server salt : [%*s]", packet->new_auth_protocol_data_len, packet->new_auth_protocol_data);
2066 	}
2067 
2068 	DBG_RETURN(PASS);
2069 premature_end:
2070 	DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
2071 	php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
2072 						 p - begin - packet->header.size);
2073 	DBG_RETURN(FAIL);
2074 }
2075 /* }}} */
2076 
2077 
2078 /* {{{ php_mysqlnd_chg_user_free_mem */
2079 static void
php_mysqlnd_chg_user_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)2080 php_mysqlnd_chg_user_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
2081 {
2082 	MYSQLND_PACKET_CHG_USER_RESPONSE * p = (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
2083 
2084 	if (p->new_auth_protocol) {
2085 		mnd_efree(p->new_auth_protocol);
2086 		p->new_auth_protocol = NULL;
2087 	}
2088 	p->new_auth_protocol_len = 0;
2089 
2090 	if (p->new_auth_protocol_data) {
2091 		mnd_efree(p->new_auth_protocol_data);
2092 		p->new_auth_protocol_data = NULL;
2093 	}
2094 	p->new_auth_protocol_data_len = 0;
2095 
2096 	if (!stack_allocation) {
2097 		mnd_pefree(p, p->header.persistent);
2098 	}
2099 }
2100 /* }}} */
2101 
2102 
2103 /* {{{ php_mysqlnd_sha256_pk_request_write */
2104 static
php_mysqlnd_sha256_pk_request_write(void * _packet,MYSQLND_CONN_DATA * conn TSRMLS_DC)2105 size_t php_mysqlnd_sha256_pk_request_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
2106 {
2107 	zend_uchar buffer[MYSQLND_HEADER_SIZE + 1];
2108 	size_t sent;
2109 
2110 	DBG_ENTER("php_mysqlnd_sha256_pk_request_write");
2111 
2112 	int1store(buffer + MYSQLND_HEADER_SIZE, '\1');
2113 	sent = conn->net->data->m.send_ex(conn->net, buffer, 1, conn->stats, conn->error_info TSRMLS_CC);
2114 
2115 	DBG_RETURN(sent);
2116 }
2117 /* }}} */
2118 
2119 
2120 /* {{{ php_mysqlnd_sha256_pk_request_free_mem */
2121 static
php_mysqlnd_sha256_pk_request_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)2122 void php_mysqlnd_sha256_pk_request_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
2123 {
2124 	if (!stack_allocation) {
2125 		MYSQLND_PACKET_SHA256_PK_REQUEST * p = (MYSQLND_PACKET_SHA256_PK_REQUEST *) _packet;
2126 		mnd_pefree(p, p->header.persistent);
2127 	}
2128 }
2129 /* }}} */
2130 
2131 
2132 #define SHA256_PK_REQUEST_RESP_BUFFER_SIZE 2048
2133 
2134 /* {{{ php_mysqlnd_sha256_pk_request_response_read */
2135 static enum_func_status
php_mysqlnd_sha256_pk_request_response_read(void * _packet,MYSQLND_CONN_DATA * conn TSRMLS_DC)2136 php_mysqlnd_sha256_pk_request_response_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC)
2137 {
2138 	zend_uchar buf[SHA256_PK_REQUEST_RESP_BUFFER_SIZE];
2139 	zend_uchar *p = buf;
2140 	zend_uchar *begin = buf;
2141 	MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * packet= (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
2142 
2143 	DBG_ENTER("php_mysqlnd_sha256_pk_request_response_read");
2144 
2145 	/* leave space for terminating safety \0 */
2146 	PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "SHA256_PK_REQUEST_RESPONSE", PROT_SHA256_PK_REQUEST_RESPONSE_PACKET);
2147 	BAIL_IF_NO_MORE_DATA;
2148 
2149 	p++;
2150 	BAIL_IF_NO_MORE_DATA;
2151 
2152 	packet->public_key_len = packet->header.size - (p - buf);
2153 	packet->public_key = mnd_emalloc(packet->public_key_len + 1);
2154 	memcpy(packet->public_key, p, packet->public_key_len);
2155 	packet->public_key[packet->public_key_len] = '\0';
2156 
2157 	DBG_RETURN(PASS);
2158 
2159 premature_end:
2160 	DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
2161 	php_error_docref(NULL TSRMLS_CC, E_WARNING, "SHA256_PK_REQUEST_RESPONSE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
2162 					 p - begin - packet->header.size);
2163 	DBG_RETURN(FAIL);
2164 }
2165 /* }}} */
2166 
2167 
2168 /* {{{ php_mysqlnd_sha256_pk_request_response_free_mem */
2169 static void
php_mysqlnd_sha256_pk_request_response_free_mem(void * _packet,zend_bool stack_allocation TSRMLS_DC)2170 php_mysqlnd_sha256_pk_request_response_free_mem(void * _packet, zend_bool stack_allocation TSRMLS_DC)
2171 {
2172 	MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE * p = (MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE *) _packet;
2173 	if (p->public_key) {
2174 		mnd_efree(p->public_key);
2175 		p->public_key = NULL;
2176 	}
2177 	p->public_key_len = 0;
2178 
2179 	if (!stack_allocation) {
2180 		mnd_pefree(p, p->header.persistent);
2181 	}
2182 }
2183 /* }}} */
2184 
2185 
2186 /* {{{ packet_methods */
2187 static
2188 mysqlnd_packet_methods packet_methods[PROT_LAST] =
2189 {
2190 	{
2191 		sizeof(MYSQLND_PACKET_GREET),
2192 		php_mysqlnd_greet_read,
2193 		NULL, /* write */
2194 		php_mysqlnd_greet_free_mem,
2195 	}, /* PROT_GREET_PACKET */
2196 	{
2197 		sizeof(MYSQLND_PACKET_AUTH),
2198 		NULL, /* read */
2199 		php_mysqlnd_auth_write,
2200 		php_mysqlnd_auth_free_mem,
2201 	}, /* PROT_AUTH_PACKET */
2202 	{
2203 		sizeof(MYSQLND_PACKET_AUTH_RESPONSE),
2204 		php_mysqlnd_auth_response_read, /* read */
2205 		NULL, /* write */
2206 		php_mysqlnd_auth_response_free_mem,
2207 	}, /* PROT_AUTH_RESP_PACKET */
2208 	{
2209 		sizeof(MYSQLND_PACKET_CHANGE_AUTH_RESPONSE),
2210 		NULL, /* read */
2211 		php_mysqlnd_change_auth_response_write, /* write */
2212 		php_mysqlnd_change_auth_response_free_mem,
2213 	}, /* PROT_CHANGE_AUTH_RESP_PACKET */
2214 	{
2215 		sizeof(MYSQLND_PACKET_OK),
2216 		php_mysqlnd_ok_read, /* read */
2217 		NULL, /* write */
2218 		php_mysqlnd_ok_free_mem,
2219 	}, /* PROT_OK_PACKET */
2220 	{
2221 		sizeof(MYSQLND_PACKET_EOF),
2222 		php_mysqlnd_eof_read, /* read */
2223 		NULL, /* write */
2224 		php_mysqlnd_eof_free_mem,
2225 	}, /* PROT_EOF_PACKET */
2226 	{
2227 		sizeof(MYSQLND_PACKET_COMMAND),
2228 		NULL, /* read */
2229 		php_mysqlnd_cmd_write, /* write */
2230 		php_mysqlnd_cmd_free_mem,
2231 	}, /* PROT_CMD_PACKET */
2232 	{
2233 		sizeof(MYSQLND_PACKET_RSET_HEADER),
2234 		php_mysqlnd_rset_header_read, /* read */
2235 		NULL, /* write */
2236 		php_mysqlnd_rset_header_free_mem,
2237 	}, /* PROT_RSET_HEADER_PACKET */
2238 	{
2239 		sizeof(MYSQLND_PACKET_RES_FIELD),
2240 		php_mysqlnd_rset_field_read, /* read */
2241 		NULL, /* write */
2242 		php_mysqlnd_rset_field_free_mem,
2243 	}, /* PROT_RSET_FLD_PACKET */
2244 	{
2245 		sizeof(MYSQLND_PACKET_ROW),
2246 		php_mysqlnd_rowp_read, /* read */
2247 		NULL, /* write */
2248 		php_mysqlnd_rowp_free_mem,
2249 	}, /* PROT_ROW_PACKET */
2250 	{
2251 		sizeof(MYSQLND_PACKET_STATS),
2252 		php_mysqlnd_stats_read, /* read */
2253 		NULL, /* write */
2254 		php_mysqlnd_stats_free_mem,
2255 	}, /* PROT_STATS_PACKET */
2256 	{
2257 		sizeof(MYSQLND_PACKET_PREPARE_RESPONSE),
2258 		php_mysqlnd_prepare_read, /* read */
2259 		NULL, /* write */
2260 		php_mysqlnd_prepare_free_mem,
2261 	}, /* PROT_PREPARE_RESP_PACKET */
2262 	{
2263 		sizeof(MYSQLND_PACKET_CHG_USER_RESPONSE),
2264 		php_mysqlnd_chg_user_read, /* read */
2265 		NULL, /* write */
2266 		php_mysqlnd_chg_user_free_mem,
2267 	}, /* PROT_CHG_USER_RESP_PACKET */
2268 	{
2269 		sizeof(MYSQLND_PACKET_SHA256_PK_REQUEST),
2270 		NULL, /* read */
2271 		php_mysqlnd_sha256_pk_request_write,
2272 		php_mysqlnd_sha256_pk_request_free_mem,
2273 	}, /* PROT_SHA256_PK_REQUEST_PACKET */
2274 	{
2275 		sizeof(MYSQLND_PACKET_SHA256_PK_REQUEST_RESPONSE),
2276 		php_mysqlnd_sha256_pk_request_response_read,
2277 		NULL, /* write */
2278 		php_mysqlnd_sha256_pk_request_response_free_mem,
2279 	} /* PROT_SHA256_PK_REQUEST_RESPONSE_PACKET */
2280 };
2281 /* }}} */
2282 
2283 
2284 /* {{{ mysqlnd_protocol::get_greet_packet */
2285 static struct st_mysqlnd_packet_greet *
MYSQLND_METHOD(mysqlnd_protocol,get_greet_packet)2286 MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2287 {
2288 	struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
2289 	DBG_ENTER("mysqlnd_protocol::get_greet_packet");
2290 	if (packet) {
2291 		packet->header.m = &packet_methods[PROT_GREET_PACKET];
2292 		packet->header.persistent = persistent;
2293 	}
2294 	DBG_RETURN(packet);
2295 }
2296 /* }}} */
2297 
2298 
2299 /* {{{ mysqlnd_protocol::get_auth_packet */
2300 static struct st_mysqlnd_packet_auth *
MYSQLND_METHOD(mysqlnd_protocol,get_auth_packet)2301 MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2302 {
2303 	struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
2304 	DBG_ENTER("mysqlnd_protocol::get_auth_packet");
2305 	if (packet) {
2306 		packet->header.m = &packet_methods[PROT_AUTH_PACKET];
2307 		packet->header.persistent = persistent;
2308 	}
2309 	DBG_RETURN(packet);
2310 }
2311 /* }}} */
2312 
2313 
2314 /* {{{ mysqlnd_protocol::get_auth_response_packet */
2315 static struct st_mysqlnd_packet_auth_response *
MYSQLND_METHOD(mysqlnd_protocol,get_auth_response_packet)2316 MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2317 {
2318 	struct st_mysqlnd_packet_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_RESP_PACKET].struct_size, persistent);
2319 	DBG_ENTER("mysqlnd_protocol::get_auth_response_packet");
2320 	if (packet) {
2321 		packet->header.m = &packet_methods[PROT_AUTH_RESP_PACKET];
2322 		packet->header.persistent = persistent;
2323 	}
2324 	DBG_RETURN(packet);
2325 }
2326 /* }}} */
2327 
2328 
2329 /* {{{ mysqlnd_protocol::get_change_auth_response_packet */
2330 static struct st_mysqlnd_packet_change_auth_response *
MYSQLND_METHOD(mysqlnd_protocol,get_change_auth_response_packet)2331 MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2332 {
2333 	struct st_mysqlnd_packet_change_auth_response * packet = mnd_pecalloc(1, packet_methods[PROT_CHANGE_AUTH_RESP_PACKET].struct_size, persistent);
2334 	DBG_ENTER("mysqlnd_protocol::get_change_auth_response_packet");
2335 	if (packet) {
2336 		packet->header.m = &packet_methods[PROT_CHANGE_AUTH_RESP_PACKET];
2337 		packet->header.persistent = persistent;
2338 	}
2339 	DBG_RETURN(packet);
2340 }
2341 /* }}} */
2342 
2343 
2344 /* {{{ mysqlnd_protocol::get_ok_packet */
2345 static struct st_mysqlnd_packet_ok *
MYSQLND_METHOD(mysqlnd_protocol,get_ok_packet)2346 MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2347 {
2348 	struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
2349 	DBG_ENTER("mysqlnd_protocol::get_ok_packet");
2350 	if (packet) {
2351 		packet->header.m = &packet_methods[PROT_OK_PACKET];
2352 		packet->header.persistent = persistent;
2353 	}
2354 	DBG_RETURN(packet);
2355 }
2356 /* }}} */
2357 
2358 
2359 /* {{{ mysqlnd_protocol::get_eof_packet */
2360 static struct st_mysqlnd_packet_eof *
MYSQLND_METHOD(mysqlnd_protocol,get_eof_packet)2361 MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2362 {
2363 	struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
2364 	DBG_ENTER("mysqlnd_protocol::get_eof_packet");
2365 	if (packet) {
2366 		packet->header.m = &packet_methods[PROT_EOF_PACKET];
2367 		packet->header.persistent = persistent;
2368 	}
2369 	DBG_RETURN(packet);
2370 }
2371 /* }}} */
2372 
2373 
2374 /* {{{ mysqlnd_protocol::get_command_packet */
2375 static struct st_mysqlnd_packet_command *
MYSQLND_METHOD(mysqlnd_protocol,get_command_packet)2376 MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2377 {
2378 	struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
2379 	DBG_ENTER("mysqlnd_protocol::get_command_packet");
2380 	if (packet) {
2381 		packet->header.m = &packet_methods[PROT_CMD_PACKET];
2382 		packet->header.persistent = persistent;
2383 	}
2384 	DBG_RETURN(packet);
2385 }
2386 /* }}} */
2387 
2388 
2389 /* {{{ mysqlnd_protocol::get_rset_packet */
2390 static struct st_mysqlnd_packet_rset_header *
MYSQLND_METHOD(mysqlnd_protocol,get_rset_header_packet)2391 MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2392 {
2393 	struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
2394 	DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
2395 	if (packet) {
2396 		packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
2397 		packet->header.persistent = persistent;
2398 	}
2399 	DBG_RETURN(packet);
2400 }
2401 /* }}} */
2402 
2403 
2404 /* {{{ mysqlnd_protocol::get_result_field_packet */
2405 static struct st_mysqlnd_packet_res_field *
MYSQLND_METHOD(mysqlnd_protocol,get_result_field_packet)2406 MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2407 {
2408 	struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
2409 	DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
2410 	if (packet) {
2411 		packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
2412 		packet->header.persistent = persistent;
2413 	}
2414 	DBG_RETURN(packet);
2415 }
2416 /* }}} */
2417 
2418 
2419 /* {{{ mysqlnd_protocol::get_row_packet */
2420 static struct st_mysqlnd_packet_row *
MYSQLND_METHOD(mysqlnd_protocol,get_row_packet)2421 MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2422 {
2423 	struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
2424 	DBG_ENTER("mysqlnd_protocol::get_row_packet");
2425 	if (packet) {
2426 		packet->header.m = &packet_methods[PROT_ROW_PACKET];
2427 		packet->header.persistent = persistent;
2428 	}
2429 	DBG_RETURN(packet);
2430 }
2431 /* }}} */
2432 
2433 
2434 /* {{{ mysqlnd_protocol::get_stats_packet */
2435 static struct st_mysqlnd_packet_stats *
MYSQLND_METHOD(mysqlnd_protocol,get_stats_packet)2436 MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2437 {
2438 	struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
2439 	DBG_ENTER("mysqlnd_protocol::get_stats_packet");
2440 	if (packet) {
2441 		packet->header.m = &packet_methods[PROT_STATS_PACKET];
2442 		packet->header.persistent = persistent;
2443 	}
2444 	DBG_RETURN(packet);
2445 }
2446 /* }}} */
2447 
2448 
2449 /* {{{ mysqlnd_protocol::get_prepare_response_packet */
2450 static struct st_mysqlnd_packet_prepare_response *
MYSQLND_METHOD(mysqlnd_protocol,get_prepare_response_packet)2451 MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2452 {
2453 	struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
2454 	DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
2455 	if (packet) {
2456 		packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
2457 		packet->header.persistent = persistent;
2458 	}
2459 	DBG_RETURN(packet);
2460 }
2461 /* }}} */
2462 
2463 
2464 /* {{{ mysqlnd_protocol::get_change_user_response_packet */
2465 static struct st_mysqlnd_packet_chg_user_resp*
MYSQLND_METHOD(mysqlnd_protocol,get_change_user_response_packet)2466 MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2467 {
2468 	struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
2469 	DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
2470 	if (packet) {
2471 		packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
2472 		packet->header.persistent = persistent;
2473 	}
2474 	DBG_RETURN(packet);
2475 }
2476 /* }}} */
2477 
2478 
2479 /* {{{ mysqlnd_protocol::get_sha256_pk_request_packet */
2480 static struct st_mysqlnd_packet_sha256_pk_request *
MYSQLND_METHOD(mysqlnd_protocol,get_sha256_pk_request_packet)2481 MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2482 {
2483 	struct st_mysqlnd_packet_sha256_pk_request * packet = mnd_pecalloc(1, packet_methods[PROT_SHA256_PK_REQUEST_PACKET].struct_size, persistent);
2484 	DBG_ENTER("mysqlnd_protocol::get_sha256_pk_request_packet");
2485 	if (packet) {
2486 		packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_PACKET];
2487 		packet->header.persistent = persistent;
2488 	}
2489 	DBG_RETURN(packet);
2490 }
2491 /* }}} */
2492 
2493 
2494 /* {{{ mysqlnd_protocol::get_sha256_pk_request_response_packet */
2495 static struct st_mysqlnd_packet_sha256_pk_request_response *
MYSQLND_METHOD(mysqlnd_protocol,get_sha256_pk_request_response_packet)2496 MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
2497 {
2498 	struct st_mysqlnd_packet_sha256_pk_request_response * packet = mnd_pecalloc(1, packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET].struct_size, persistent);
2499 	DBG_ENTER("mysqlnd_protocol::get_sha256_pk_request_response_packet");
2500 	if (packet) {
2501 		packet->header.m = &packet_methods[PROT_SHA256_PK_REQUEST_RESPONSE_PACKET];
2502 		packet->header.persistent = persistent;
2503 	}
2504 	DBG_RETURN(packet);
2505 }
2506 /* }}} */
2507 
2508 
2509 
2510 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
2511 	MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
2512 	MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
2513 	MYSQLND_METHOD(mysqlnd_protocol, get_auth_response_packet),
2514 	MYSQLND_METHOD(mysqlnd_protocol, get_change_auth_response_packet),
2515 	MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet),
2516 	MYSQLND_METHOD(mysqlnd_protocol, get_command_packet),
2517 	MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet),
2518 	MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet),
2519 	MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet),
2520 	MYSQLND_METHOD(mysqlnd_protocol, get_row_packet),
2521 	MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet),
2522 	MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
2523 	MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet),
2524 	MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_packet),
2525 	MYSQLND_METHOD(mysqlnd_protocol, get_sha256_pk_request_response_packet)
2526 MYSQLND_CLASS_METHODS_END;
2527 
2528 
2529 /* {{{ mysqlnd_protocol_init */
2530 PHPAPI MYSQLND_PROTOCOL *
mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)2531 mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)
2532 {
2533 	MYSQLND_PROTOCOL * ret;
2534 	DBG_ENTER("mysqlnd_protocol_init");
2535 	ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_protocol_decoder(persistent TSRMLS_CC);
2536 	DBG_RETURN(ret);
2537 }
2538 /* }}} */
2539 
2540 
2541 /* {{{ mysqlnd_protocol_free */
2542 PHPAPI void
mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)2543 mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)
2544 {
2545 	DBG_ENTER("mysqlnd_protocol_free");
2546 
2547 	if (protocol) {
2548 		zend_bool pers = protocol->persistent;
2549 		mnd_pefree(protocol, pers);
2550 	}
2551 	DBG_VOID_RETURN;
2552 }
2553 /* }}} */
2554 
2555 
2556 /*
2557  * Local variables:
2558  * tab-width: 4
2559  * c-basic-offset: 4
2560  * End:
2561  * vim600: noet sw=4 ts=4 fdm=marker
2562  * vim<600: noet sw=4 ts=4
2563  */
2564