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