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