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