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