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