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