1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Andrey Hristov <andrey@php.net>                             |
14   |          Ulf Wendel <uw@php.net>                                     |
15   +----------------------------------------------------------------------+
16 */
17 
18 #include "php.h"
19 #include "mysqlnd.h"
20 #include "mysqlnd_connection.h"
21 #include "mysqlnd_priv.h"
22 #include "mysqlnd_read_buffer.h"
23 #include "mysqlnd_wireprotocol.h"
24 #include "mysqlnd_statistics.h"
25 #include "mysqlnd_debug.h"
26 #ifdef MYSQLND_COMPRESSION_ENABLED
27 #include <zlib.h>
28 #endif
29 
30 
31 /* {{{ mysqlnd_pfc::reset */
32 static void
MYSQLND_METHOD(mysqlnd_pfc,reset)33 MYSQLND_METHOD(mysqlnd_pfc, reset)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
34 {
35 	DBG_ENTER("mysqlnd_pfc::reset");
36 	pfc->data->packet_no = pfc->data->compressed_envelope_packet_no = 0;
37 	DBG_VOID_RETURN;
38 }
39 /* }}} */
40 
41 
42 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
43 #define COPY_HEADER(T,A)  do { \
44 		*(((char *)(T)))   = *(((char *)(A)));\
45 		*(((char *)(T))+1) = *(((char *)(A))+1);\
46 		*(((char *)(T))+2) = *(((char *)(A))+2);\
47 		*(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
48 #define STORE_HEADER_SIZE(safe_storage, buffer)  COPY_HEADER((safe_storage), (buffer))
49 #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
50 
51 #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)52 static ssize_t write_compressed_packet(
53 		const MYSQLND_PFC *pfc, MYSQLND_VIO *vio,
54 		MYSQLND_STATS *conn_stats, MYSQLND_ERROR_INFO *error_info,
55 		zend_uchar *uncompressed_payload, size_t to_be_sent, zend_uchar *compress_buf) {
56 	DBG_ENTER("write_compressed_packet");
57 	/* here we need to compress the data and then write it, first comes the compressed header */
58 	size_t tmp_complen = to_be_sent;
59 	size_t payload_size;
60 	if (PASS == pfc->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
61 							   uncompressed_payload, to_be_sent))
62 	{
63 		int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent);
64 		payload_size = tmp_complen;
65 	} else {
66 		int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
67 		memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent);
68 		payload_size = to_be_sent;
69 	}
70 
71 	int3store(compress_buf, payload_size);
72 	int1store(compress_buf + 3, pfc->data->compressed_envelope_packet_no);
73 	DBG_INF_FMT("writing %zu bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
74 
75 	ssize_t bytes_sent = vio->data->m.network_write(vio, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, conn_stats, error_info);
76 	pfc->data->compressed_envelope_packet_no++;
77 #ifdef WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
78 	if (res == Z_OK) {
79 		size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
80 		zend_uchar * decompressed_data = mnd_emalloc(decompressed_size);
81 		int error = pfc->data->m.decode(decompressed_data, decompressed_size,
82 										compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
83 		if (error == Z_OK) {
84 			int i;
85 			DBG_INF("success decompressing");
86 			for (i = 0 ; i < decompressed_size; i++) {
87 				if (i && (i % 30 == 0)) {
88 					printf("\n\t\t");
89 				}
90 				printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
91 				DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
92 			}
93 		} else {
94 			DBG_INF("error decompressing");
95 		}
96 		mnd_efree(decompressed_data);
97 	}
98 #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
99 	DBG_RETURN(bytes_sent);
100 }
101 #endif
102 
103 /* {{{ mysqlnd_pfc::send */
104 /*
105   IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
106 			  This is done for performance reasons in the caller of this function.
107 			  Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
108 			  Neither are quick, thus the clients of this function are obligated to do
109 			  what they are asked for.
110 
111   `count` is actually the length of the payload data. Thus :
112   count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
113 */
114 static size_t
MYSQLND_METHOD(mysqlnd_pfc,send)115 MYSQLND_METHOD(mysqlnd_pfc, send)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
116 								  MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
117 {
118 	zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
119 	zend_uchar * safe_storage = safe_buf;
120 	size_t packets_sent = 1;
121 	size_t left = count;
122 	zend_uchar * p = (zend_uchar *) buffer;
123 	zend_uchar * compress_buf = NULL;
124 	size_t to_be_sent;
125 	ssize_t bytes_sent;
126 
127 	DBG_ENTER("mysqlnd_pfc::send");
128 	DBG_INF_FMT("count=%zu compression=%u", count, pfc->data->compressed);
129 
130 	if (pfc->data->compressed == TRUE) {
131 		size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
132 		DBG_INF_FMT("compress_buf_size=%zu", comp_buf_size);
133 		compress_buf = mnd_emalloc(comp_buf_size);
134 	}
135 
136 	do {
137 		to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
138 		DBG_INF_FMT("to_be_sent=%zu", to_be_sent);
139 		DBG_INF_FMT("packets_sent=%zu", packets_sent);
140 		DBG_INF_FMT("compressed_envelope_packet_no=%u", pfc->data->compressed_envelope_packet_no);
141 		DBG_INF_FMT("packet_no=%u", pfc->data->packet_no);
142 #ifdef MYSQLND_COMPRESSION_ENABLED
143 		if (pfc->data->compressed == TRUE) {
144 			zend_uchar * uncompressed_payload = p; /* should include the header */
145 			STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
146 			int3store(uncompressed_payload, to_be_sent);
147 			int1store(uncompressed_payload + 3, pfc->data->packet_no);
148 			if (to_be_sent <= MYSQLND_MAX_PACKET_SIZE - MYSQLND_HEADER_SIZE) {
149 				bytes_sent = write_compressed_packet(
150 					pfc, vio, conn_stats, error_info,
151 					uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE, compress_buf);
152 			} else {
153 				/* The uncompressed size including the header would overflow. Split into two
154 				 * compressed packets. The size of the first one is relatively arbitrary here. */
155 				const size_t split_off_bytes = 8192;
156 				bytes_sent = write_compressed_packet(
157 					pfc, vio, conn_stats, error_info,
158 					uncompressed_payload, split_off_bytes, compress_buf);
159 				bytes_sent = write_compressed_packet(
160 					pfc, vio, conn_stats, error_info,
161 					uncompressed_payload + split_off_bytes,
162 					to_be_sent + MYSQLND_HEADER_SIZE - split_off_bytes, compress_buf);
163 			}
164 			RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
165 		} else
166 #endif /* MYSQLND_COMPRESSION_ENABLED */
167 		{
168 			DBG_INF("no compression");
169 			STORE_HEADER_SIZE(safe_storage, p);
170 			int3store(p, to_be_sent);
171 			int1store(p + 3, pfc->data->packet_no);
172 			bytes_sent = vio->data->m.network_write(vio, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info);
173 			RESTORE_HEADER_SIZE(p, safe_storage);
174 			pfc->data->compressed_envelope_packet_no++;
175 		}
176 		pfc->data->packet_no++;
177 
178 		p += to_be_sent;
179 		left -= to_be_sent;
180 		packets_sent++;
181 		/*
182 		  if left is 0 then there is nothing more to send, but if the last packet was exactly
183 		  with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
184 		  empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
185 		  indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
186 		  packet will be sent and this loop will end.
187 		*/
188 	} while (bytes_sent > 0 && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
189 
190 	DBG_INF_FMT("packet_size=%zu packet_no=%u", left, pfc->data->packet_no);
191 
192 	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
193 			STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
194 			STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
195 			STAT_PACKETS_SENT, packets_sent);
196 
197 	if (compress_buf) {
198 		mnd_efree(compress_buf);
199 	}
200 
201 	/* Even for zero size payload we have to send a packet */
202 	if (bytes_sent <= 0) {
203 		DBG_ERR_FMT("Can't %zu send bytes", count);
204 		SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
205 		bytes_sent = 0; // the return type is unsigned and 0 represents an error condition
206 	}
207 	DBG_RETURN(bytes_sent);
208 }
209 /* }}} */
210 
211 
212 #ifdef MYSQLND_COMPRESSION_ENABLED
213 
214 /* {{{ mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer */
215 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,read_compressed_packet_from_stream_and_fill_read_buffer)216 MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer)
217 		(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
218 {
219 	size_t decompressed_size;
220 	enum_func_status retval = PASS;
221 	zend_uchar * compressed_data = NULL;
222 	zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
223 	DBG_ENTER("mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer");
224 
225 	/* Read the compressed header */
226 	if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
227 		DBG_RETURN(FAIL);
228 	}
229 	decompressed_size = uint3korr(comp_header);
230 
231 	/* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
232 	/* we need to decompress the data */
233 
234 	if (decompressed_size) {
235 		compressed_data = mnd_emalloc(net_payload_size);
236 		if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) {
237 			retval = FAIL;
238 			goto end;
239 		}
240 		pfc->data->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
241 		retval = pfc->data->m.decode(pfc->data->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
242 		if (FAIL == retval) {
243 			goto end;
244 		}
245 	} else {
246 		DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %zu bytes", net_payload_size);
247 		pfc->data->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
248 		if (FAIL == vio->data->m.network_read(vio, pfc->data->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
249 			retval = FAIL;
250 			goto end;
251 		}
252 	}
253 end:
254 	if (compressed_data) {
255 		mnd_efree(compressed_data);
256 	}
257 	DBG_RETURN(retval);
258 }
259 /* }}} */
260 #endif /* MYSQLND_COMPRESSION_ENABLED */
261 
262 
263 /* {{{ mysqlnd_pfc::decode */
264 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,decode)265 MYSQLND_METHOD(mysqlnd_pfc, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
266 									const zend_uchar * const compressed_data, const size_t compressed_data_len)
267 {
268 #ifdef MYSQLND_COMPRESSION_ENABLED
269 	int error;
270 	uLongf tmp_complen = uncompressed_data_len;
271 	DBG_ENTER("mysqlnd_pfc::decode");
272 	error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
273 
274 	DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size=%zu", tmp_complen, compressed_data_len);
275 	if (error != Z_OK) {
276 		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);
277 	}
278 	DBG_RETURN(error == Z_OK? PASS:FAIL);
279 #else
280 	DBG_ENTER("mysqlnd_pfc::decode");
281 	DBG_RETURN(FAIL);
282 #endif
283 }
284 /* }}} */
285 
286 
287 /* {{{ mysqlnd_pfc::encode */
288 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,encode)289 MYSQLND_METHOD(mysqlnd_pfc, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
290 									const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len)
291 {
292 #ifdef MYSQLND_COMPRESSION_ENABLED
293 	int error;
294 	uLongf tmp_complen = *compress_buffer_len;
295 	DBG_ENTER("mysqlnd_pfc::encode");
296 	error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
297 
298 	if (error != Z_OK) {
299 		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);
300 	} else {
301 		*compress_buffer_len = tmp_complen;
302 		DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
303 	}
304 
305 	DBG_RETURN(error == Z_OK? PASS:FAIL);
306 #else
307 	DBG_ENTER("mysqlnd_pfc::encode");
308 	DBG_RETURN(FAIL);
309 #endif
310 }
311 /* }}} */
312 
313 
314 /* {{{ mysqlnd_pfc::receive */
315 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,receive)316 MYSQLND_METHOD(mysqlnd_pfc, receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
317 									 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
318 {
319 	size_t to_read = count;
320 	zend_uchar * p = buffer;
321 
322 	DBG_ENTER("mysqlnd_pfc::receive");
323 #ifdef MYSQLND_COMPRESSION_ENABLED
324 	if (pfc->data->compressed) {
325 		if (pfc->data->uncompressed_data) {
326 			size_t to_read_from_buffer = MIN(pfc->data->uncompressed_data->bytes_left(pfc->data->uncompressed_data), to_read);
327 			DBG_INF_FMT("reading %zu from uncompressed_data buffer", to_read_from_buffer);
328 			if (to_read_from_buffer) {
329 				pfc->data->uncompressed_data->read(pfc->data->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
330 				p += to_read_from_buffer;
331 				to_read -= to_read_from_buffer;
332 			}
333 			DBG_INF_FMT("left %zu to read", to_read);
334 			if (TRUE == pfc->data->uncompressed_data->is_empty(pfc->data->uncompressed_data)) {
335 				/* Everything was consumed. This should never happen here, but for security */
336 				pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
337 			}
338 		}
339 		if (to_read) {
340 			zend_uchar net_header[MYSQLND_HEADER_SIZE];
341 			size_t net_payload_size;
342 			zend_uchar packet_no;
343 
344 			if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
345 				DBG_RETURN(FAIL);
346 			}
347 			net_payload_size = uint3korr(net_header);
348 			packet_no = uint1korr(net_header + 3);
349 			if (pfc->data->compressed_envelope_packet_no != packet_no) {
350 				DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size=%zu",
351 							pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
352 
353 				php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size=%zu",
354 						  pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
355 				DBG_RETURN(FAIL);
356 			}
357 			pfc->data->compressed_envelope_packet_no++;
358 #ifdef MYSQLND_DUMP_HEADER_N_BODY
359 			DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size);
360 #endif
361 			/* Now let's read from the wire, decompress it and fill the read buffer */
362 			pfc->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(pfc, vio, net_payload_size, conn_stats, error_info);
363 
364 			/*
365 			  Now a bit of recursion - read from the read buffer,
366 			  if the data which we have just read from the wire
367 			  is not enough, then the recursive call will try to
368 			  satisfy it until it is satisfied.
369 			*/
370 			DBG_RETURN(pfc->data->m.receive(pfc, vio, p, to_read, conn_stats, error_info));
371 		}
372 		DBG_RETURN(PASS);
373 	}
374 #endif /* MYSQLND_COMPRESSION_ENABLED */
375 	DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info));
376 }
377 /* }}} */
378 
379 
380 /* {{{ mysqlnd_pfc::set_client_option */
381 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,set_client_option)382 MYSQLND_METHOD(mysqlnd_pfc, set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value)
383 {
384 	DBG_ENTER("mysqlnd_pfc::set_client_option");
385 	DBG_INF_FMT("option=%u", option);
386 	switch (option) {
387 		case MYSQL_OPT_COMPRESS:
388 			pfc->data->flags |= MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION;
389 			break;
390 		case MYSQL_SERVER_PUBLIC_KEY: {
391 			const bool pers = pfc->persistent;
392 			if (pfc->data->sha256_server_public_key) {
393 				mnd_pefree(pfc->data->sha256_server_public_key, pers);
394 			}
395 			pfc->data->sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
396 			break;
397 		}
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=%zu", 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 void
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_VOID_RETURN;
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 bool persistent,MYSQLND_CLASS_METHODS_TYPE (mysqlnd_object_factory)* object_factory,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)502 mysqlnd_pfc_init(const 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