1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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_priv.h"
24 #include "mysqlnd_read_buffer.h"
25 #include "mysqlnd_wireprotocol.h"
26 #include "mysqlnd_statistics.h"
27 #include "mysqlnd_debug.h"
28 #ifdef MYSQLND_COMPRESSION_ENABLED
29 #include <zlib.h>
30 #endif
31 
32 
33 /* {{{ mysqlnd_pfc::reset */
34 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,reset)35 MYSQLND_METHOD(mysqlnd_pfc, reset)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
36 {
37 	DBG_ENTER("mysqlnd_pfc::reset");
38 	pfc->data->packet_no = pfc->data->compressed_envelope_packet_no = 0;
39 	DBG_RETURN(PASS);
40 }
41 /* }}} */
42 
43 
44 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
45 #define COPY_HEADER(T,A)  do { \
46 		*(((char *)(T)))   = *(((char *)(A)));\
47 		*(((char *)(T))+1) = *(((char *)(A))+1);\
48 		*(((char *)(T))+2) = *(((char *)(A))+2);\
49 		*(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
50 #define STORE_HEADER_SIZE(safe_storage, buffer)  COPY_HEADER((safe_storage), (buffer))
51 #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
52 
53 #ifdef MYSQLND_COMPRESSION_ENABLED
write_compressed_packet(const MYSQLND_PFC * pfc,MYSQLND_VIO * vio,MYSQLND_STATS * conn_stats,MYSQLND_ERROR_INFO * error_info,zend_uchar * uncompressed_payload,size_t to_be_sent,zend_uchar * compress_buf)54 static ssize_t write_compressed_packet(
55 		const MYSQLND_PFC *pfc, MYSQLND_VIO *vio,
56 		MYSQLND_STATS *conn_stats, MYSQLND_ERROR_INFO *error_info,
57 		zend_uchar *uncompressed_payload, size_t to_be_sent, zend_uchar *compress_buf) {
58 	DBG_ENTER("write_compressed_packet");
59 	/* here we need to compress the data and then write it, first comes the compressed header */
60 	size_t tmp_complen = to_be_sent;
61 	size_t payload_size;
62 	if (PASS == pfc->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
63 							   uncompressed_payload, to_be_sent))
64 	{
65 		int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent);
66 		payload_size = tmp_complen;
67 	} else {
68 		int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
69 		memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent);
70 		payload_size = to_be_sent;
71 	}
72 
73 	int3store(compress_buf, payload_size);
74 	int1store(compress_buf + 3, pfc->data->compressed_envelope_packet_no);
75 	DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
76 
77 	ssize_t bytes_sent = vio->data->m.network_write(vio, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, conn_stats, error_info);
78 	pfc->data->compressed_envelope_packet_no++;
79 #ifdef WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
80 	if (res == Z_OK) {
81 		size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
82 		zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
83 		int error = pfc->data->m.decode(decompressed_data, decompressed_size,
84 										compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
85 		if (error == Z_OK) {
86 			int i;
87 			DBG_INF("success decompressing");
88 			for (i = 0 ; i < decompressed_size; i++) {
89 				if (i && (i % 30 == 0)) {
90 					printf("\n\t\t");
91 				}
92 				printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
93 				DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
94 			}
95 		} else {
96 			DBG_INF("error decompressing");
97 		}
98 		mnd_free(decompressed_data);
99 	}
100 #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
101 	DBG_RETURN(bytes_sent);
102 }
103 #endif
104 
105 /* {{{ mysqlnd_pfc::send */
106 /*
107   IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
108 			  This is done for performance reasons in the caller of this function.
109 			  Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
110 			  Neither are quick, thus the clients of this function are obligated to do
111 			  what they are asked for.
112 
113   `count` is actually the length of the payload data. Thus :
114   count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
115 */
116 static size_t
MYSQLND_METHOD(mysqlnd_pfc,send)117 MYSQLND_METHOD(mysqlnd_pfc, send)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
118 								  MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
119 {
120 	zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
121 	zend_uchar * safe_storage = safe_buf;
122 	size_t packets_sent = 1;
123 	size_t left = count;
124 	zend_uchar * p = (zend_uchar *) buffer;
125 	zend_uchar * compress_buf = NULL;
126 	size_t to_be_sent;
127 	ssize_t bytes_sent;
128 
129 	DBG_ENTER("mysqlnd_pfc::send");
130 	DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, pfc->data->compressed);
131 
132 	if (pfc->data->compressed == TRUE) {
133 		size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
134 		DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
135 		compress_buf = mnd_emalloc(comp_buf_size);
136 	}
137 
138 	do {
139 		to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
140 		DBG_INF_FMT("to_be_sent=%u", to_be_sent);
141 		DBG_INF_FMT("packets_sent=%u", packets_sent);
142 		DBG_INF_FMT("compressed_envelope_packet_no=%u", pfc->data->compressed_envelope_packet_no);
143 		DBG_INF_FMT("packet_no=%u", pfc->data->packet_no);
144 #ifdef MYSQLND_COMPRESSION_ENABLED
145 		if (pfc->data->compressed == TRUE) {
146 			zend_uchar * uncompressed_payload = p; /* should include the header */
147 			STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
148 			int3store(uncompressed_payload, to_be_sent);
149 			int1store(uncompressed_payload + 3, pfc->data->packet_no);
150 			if (to_be_sent <= MYSQLND_MAX_PACKET_SIZE - MYSQLND_HEADER_SIZE) {
151 				bytes_sent = write_compressed_packet(
152 					pfc, vio, conn_stats, error_info,
153 					uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE, compress_buf);
154 			} else {
155 				/* The uncompressed size including the header would overflow. Split into two
156 				 * compressed packets. The size of the first one is relatively arbitrary here. */
157 				const size_t split_off_bytes = 8192;
158 				bytes_sent = write_compressed_packet(
159 					pfc, vio, conn_stats, error_info,
160 					uncompressed_payload, split_off_bytes, compress_buf);
161 				bytes_sent = write_compressed_packet(
162 					pfc, vio, conn_stats, error_info,
163 					uncompressed_payload + split_off_bytes,
164 					to_be_sent + MYSQLND_HEADER_SIZE - split_off_bytes, compress_buf);
165 			}
166 			RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
167 		} else
168 #endif /* MYSQLND_COMPRESSION_ENABLED */
169 		{
170 			DBG_INF("no compression");
171 			STORE_HEADER_SIZE(safe_storage, p);
172 			int3store(p, to_be_sent);
173 			int1store(p + 3, pfc->data->packet_no);
174 			bytes_sent = vio->data->m.network_write(vio, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info);
175 			RESTORE_HEADER_SIZE(p, safe_storage);
176 			pfc->data->compressed_envelope_packet_no++;
177 		}
178 		pfc->data->packet_no++;
179 
180 		p += to_be_sent;
181 		left -= to_be_sent;
182 		packets_sent++;
183 		/*
184 		  if left is 0 then there is nothing more to send, but if the last packet was exactly
185 		  with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
186 		  empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
187 		  indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
188 		  packet will be sent and this loop will end.
189 		*/
190 	} while (bytes_sent > 0 && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
191 
192 	DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, pfc->data->packet_no);
193 
194 	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
195 			STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
196 			STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
197 			STAT_PACKETS_SENT, packets_sent);
198 
199 	if (compress_buf) {
200 		mnd_efree(compress_buf);
201 	}
202 
203 	/* Even for zero size payload we have to send a packet */
204 	if (bytes_sent <= 0) {
205 		DBG_ERR_FMT("Can't %u send bytes", count);
206 		SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
207 	}
208 	DBG_RETURN(bytes_sent);
209 }
210 /* }}} */
211 
212 
213 #ifdef MYSQLND_COMPRESSION_ENABLED
214 
215 /* {{{ mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer */
216 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,read_compressed_packet_from_stream_and_fill_read_buffer)217 MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer)
218 		(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
219 {
220 	size_t decompressed_size;
221 	enum_func_status retval = PASS;
222 	zend_uchar * compressed_data = NULL;
223 	zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
224 	DBG_ENTER("mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer");
225 
226 	/* Read the compressed header */
227 	if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
228 		DBG_RETURN(FAIL);
229 	}
230 	decompressed_size = uint3korr(comp_header);
231 
232 	/* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
233 	/* we need to decompress the data */
234 
235 	if (decompressed_size) {
236 		compressed_data = mnd_emalloc(net_payload_size);
237 		if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) {
238 			retval = FAIL;
239 			goto end;
240 		}
241 		pfc->data->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
242 		retval = pfc->data->m.decode(pfc->data->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
243 		if (FAIL == retval) {
244 			goto end;
245 		}
246 	} else {
247 		DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
248 		pfc->data->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
249 		if (FAIL == vio->data->m.network_read(vio, pfc->data->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
250 			retval = FAIL;
251 			goto end;
252 		}
253 	}
254 end:
255 	if (compressed_data) {
256 		mnd_efree(compressed_data);
257 	}
258 	DBG_RETURN(retval);
259 }
260 /* }}} */
261 #endif /* MYSQLND_COMPRESSION_ENABLED */
262 
263 
264 /* {{{ mysqlnd_pfc::decode */
265 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,decode)266 MYSQLND_METHOD(mysqlnd_pfc, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
267 									const zend_uchar * const compressed_data, const size_t compressed_data_len)
268 {
269 #ifdef MYSQLND_COMPRESSION_ENABLED
270 	int error;
271 	uLongf tmp_complen = uncompressed_data_len;
272 	DBG_ENTER("mysqlnd_pfc::decode");
273 	error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
274 
275 	DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
276 	if (error != Z_OK) {
277 		DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
278 	}
279 	DBG_RETURN(error == Z_OK? PASS:FAIL);
280 #else
281 	DBG_ENTER("mysqlnd_pfc::decode");
282 	DBG_RETURN(FAIL);
283 #endif
284 }
285 /* }}} */
286 
287 
288 /* {{{ mysqlnd_pfc::encode */
289 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,encode)290 MYSQLND_METHOD(mysqlnd_pfc, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
291 									const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len)
292 {
293 #ifdef MYSQLND_COMPRESSION_ENABLED
294 	int error;
295 	uLongf tmp_complen = *compress_buffer_len;
296 	DBG_ENTER("mysqlnd_pfc::encode");
297 	error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
298 
299 	if (error != Z_OK) {
300 		DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
301 	} else {
302 		*compress_buffer_len = tmp_complen;
303 		DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
304 	}
305 
306 	DBG_RETURN(error == Z_OK? PASS:FAIL);
307 #else
308 	DBG_ENTER("mysqlnd_pfc::encode");
309 	DBG_RETURN(FAIL);
310 #endif
311 }
312 /* }}} */
313 
314 
315 /* {{{ mysqlnd_pfc::receive */
316 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,receive)317 MYSQLND_METHOD(mysqlnd_pfc, receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
318 									 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
319 {
320 	size_t to_read = count;
321 	zend_uchar * p = buffer;
322 
323 	DBG_ENTER("mysqlnd_pfc::receive");
324 #ifdef MYSQLND_COMPRESSION_ENABLED
325 	if (pfc->data->compressed) {
326 		if (pfc->data->uncompressed_data) {
327 			size_t to_read_from_buffer = MIN(pfc->data->uncompressed_data->bytes_left(pfc->data->uncompressed_data), to_read);
328 			DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer);
329 			if (to_read_from_buffer) {
330 				pfc->data->uncompressed_data->read(pfc->data->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
331 				p += to_read_from_buffer;
332 				to_read -= to_read_from_buffer;
333 			}
334 			DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read);
335 			if (TRUE == pfc->data->uncompressed_data->is_empty(pfc->data->uncompressed_data)) {
336 				/* Everything was consumed. This should never happen here, but for security */
337 				pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
338 			}
339 		}
340 		if (to_read) {
341 			zend_uchar net_header[MYSQLND_HEADER_SIZE];
342 			size_t net_payload_size;
343 			zend_uchar packet_no;
344 
345 			if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
346 				DBG_RETURN(FAIL);
347 			}
348 			net_payload_size = uint3korr(net_header);
349 			packet_no = uint1korr(net_header + 3);
350 			if (pfc->data->compressed_envelope_packet_no != packet_no) {
351 				DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
352 							pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
353 
354 				php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
355 						  pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
356 				DBG_RETURN(FAIL);
357 			}
358 			pfc->data->compressed_envelope_packet_no++;
359 #ifdef MYSQLND_DUMP_HEADER_N_BODY
360 			DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size);
361 #endif
362 			/* Now let's read from the wire, decompress it and fill the read buffer */
363 			pfc->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(pfc, vio, net_payload_size, conn_stats, error_info);
364 
365 			/*
366 			  Now a bit of recursion - read from the read buffer,
367 			  if the data which we have just read from the wire
368 			  is not enough, then the recursive call will try to
369 			  satisfy it until it is satisfied.
370 			*/
371 			DBG_RETURN(pfc->data->m.receive(pfc, vio, p, to_read, conn_stats, error_info));
372 		}
373 		DBG_RETURN(PASS);
374 	}
375 #endif /* MYSQLND_COMPRESSION_ENABLED */
376 	DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info));
377 }
378 /* }}} */
379 
380 
381 /* {{{ mysqlnd_pfc::set_client_option */
382 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,set_client_option)383 MYSQLND_METHOD(mysqlnd_pfc, set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value)
384 {
385 	DBG_ENTER("mysqlnd_pfc::set_client_option");
386 	DBG_INF_FMT("option=%u", option);
387 	switch (option) {
388 		case MYSQL_OPT_COMPRESS:
389 			pfc->data->flags |= MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION;
390 			break;
391 		case MYSQL_SERVER_PUBLIC_KEY: {
392 			const zend_bool pers = pfc->persistent;
393 			if (pfc->data->sha256_server_public_key) {
394 				mnd_pefree(pfc->data->sha256_server_public_key, pers);
395 			}
396 			pfc->data->sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
397 			break;
398 		case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
399 			DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
400 			if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
401 				DBG_RETURN(FAIL);
402 			}
403 			pfc->cmd_buffer.length = *(unsigned int*) value;
404 			DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, pfc->cmd_buffer.length);
405 			if (!pfc->cmd_buffer.buffer) {
406 				pfc->cmd_buffer.buffer = mnd_pemalloc(pfc->cmd_buffer.length, pfc->persistent);
407 			} else {
408 				pfc->cmd_buffer.buffer = mnd_perealloc(pfc->cmd_buffer.buffer, pfc->cmd_buffer.length, pfc->persistent);
409 			}
410 			break;
411 		}
412 		default:
413 			DBG_RETURN(FAIL);
414 	}
415 	DBG_RETURN(PASS);
416 }
417 /* }}} */
418 
419 
420 /* {{{ mysqlnd_pfc::free_contents */
421 static void
MYSQLND_METHOD(mysqlnd_pfc,free_contents)422 MYSQLND_METHOD(mysqlnd_pfc, free_contents)(MYSQLND_PFC * pfc)
423 {
424 	DBG_ENTER("mysqlnd_pfc::free_contents");
425 
426 #ifdef MYSQLND_COMPRESSION_ENABLED
427 	if (pfc->data->uncompressed_data) {
428 		pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
429 	}
430 #endif
431 	if (pfc->data->sha256_server_public_key) {
432 		mnd_pefree(pfc->data->sha256_server_public_key, pfc->persistent);
433 		pfc->data->sha256_server_public_key = NULL;
434 	}
435 
436 	DBG_VOID_RETURN;
437 }
438 /* }}} */
439 
440 
441 /* {{{ mysqlnd_pfc::init */
442 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,init)443 MYSQLND_METHOD(mysqlnd_pfc, init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
444 {
445 	unsigned int buf_size;
446 	DBG_ENTER("mysqlnd_pfc::init");
447 
448 	buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
449 	pfc->data->m.set_client_option(pfc, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
450 
451 	DBG_RETURN(PASS);
452 }
453 /* }}} */
454 
455 
456 /* {{{ mysqlnd_pfc::dtor */
457 static void
MYSQLND_METHOD(mysqlnd_pfc,dtor)458 MYSQLND_METHOD(mysqlnd_pfc, dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
459 {
460 	DBG_ENTER("mysqlnd_pfc::dtor");
461 	if (pfc) {
462 		pfc->data->m.free_contents(pfc);
463 
464 		if (pfc->cmd_buffer.buffer) {
465 			DBG_INF("Freeing cmd buffer");
466 			mnd_pefree(pfc->cmd_buffer.buffer, pfc->persistent);
467 			pfc->cmd_buffer.buffer = NULL;
468 		}
469 
470 		mnd_pefree(pfc, pfc->persistent);
471 	}
472 	DBG_VOID_RETURN;
473 }
474 /* }}} */
475 
476 
477 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_packet_frame_codec)
478 	MYSQLND_METHOD(mysqlnd_pfc, init),
479 	MYSQLND_METHOD(mysqlnd_pfc, dtor),
480 	MYSQLND_METHOD(mysqlnd_pfc, reset),
481 
482 	MYSQLND_METHOD(mysqlnd_pfc, set_client_option),
483 
484 	MYSQLND_METHOD(mysqlnd_pfc, decode),
485 	MYSQLND_METHOD(mysqlnd_pfc, encode),
486 
487 	MYSQLND_METHOD(mysqlnd_pfc, send),
488 	MYSQLND_METHOD(mysqlnd_pfc, receive),
489 
490 #ifdef MYSQLND_COMPRESSION_ENABLED
491 	MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer),
492 #else
493 	NULL,
494 #endif
495 
496 	MYSQLND_METHOD(mysqlnd_pfc, free_contents),
497 MYSQLND_CLASS_METHODS_END;
498 
499 
500 /* {{{ mysqlnd_pfc_init */
501 PHPAPI MYSQLND_PFC *
mysqlnd_pfc_init(const zend_bool persistent,MYSQLND_CLASS_METHODS_TYPE (mysqlnd_object_factory)* object_factory,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)502 mysqlnd_pfc_init(const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
503 {
504 	MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
505 	MYSQLND_PFC * pfc;
506 	DBG_ENTER("mysqlnd_pfc_init");
507 	pfc = factory->get_protocol_frame_codec(persistent, stats, error_info);
508 	DBG_RETURN(pfc);
509 }
510 /* }}} */
511 
512 
513 /* {{{ mysqlnd_pfc_free */
514 PHPAPI void
mysqlnd_pfc_free(MYSQLND_PFC * const pfc,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)515 mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
516 {
517 	DBG_ENTER("mysqlnd_pfc_free");
518 	if (pfc) {
519 		pfc->data->m.dtor(pfc, stats, error_info);
520 	}
521 	DBG_VOID_RETURN;
522 }
523 /* }}} */
524