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