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