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