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   | http://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 enum_func_status
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_RETURN(PASS);
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 "MYSQLND_SZ_T_SPEC" 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_malloc(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_free(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=" MYSQLND_SZ_T_SPEC " 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="MYSQLND_SZ_T_SPEC, 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=%u", to_be_sent);
139 		DBG_INF_FMT("packets_sent=%u", 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="MYSQLND_SZ_T_SPEC" 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 %u send bytes", count);
204 		SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
205 	}
206 	DBG_RETURN(bytes_sent);
207 }
208 /* }}} */
209 
210 
211 #ifdef MYSQLND_COMPRESSION_ENABLED
212 
213 /* {{{ mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer */
214 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,read_compressed_packet_from_stream_and_fill_read_buffer)215 MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer)
216 		(MYSQLND_PFC * pfc, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
217 {
218 	size_t decompressed_size;
219 	enum_func_status retval = PASS;
220 	zend_uchar * compressed_data = NULL;
221 	zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
222 	DBG_ENTER("mysqlnd_pfc::read_compressed_packet_from_stream_and_fill_read_buffer");
223 
224 	/* Read the compressed header */
225 	if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
226 		DBG_RETURN(FAIL);
227 	}
228 	decompressed_size = uint3korr(comp_header);
229 
230 	/* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
231 	/* we need to decompress the data */
232 
233 	if (decompressed_size) {
234 		compressed_data = mnd_emalloc(net_payload_size);
235 		if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) {
236 			retval = FAIL;
237 			goto end;
238 		}
239 		pfc->data->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
240 		retval = pfc->data->m.decode(pfc->data->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
241 		if (FAIL == retval) {
242 			goto end;
243 		}
244 	} else {
245 		DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
246 		pfc->data->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
247 		if (FAIL == vio->data->m.network_read(vio, pfc->data->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
248 			retval = FAIL;
249 			goto end;
250 		}
251 	}
252 end:
253 	if (compressed_data) {
254 		mnd_efree(compressed_data);
255 	}
256 	DBG_RETURN(retval);
257 }
258 /* }}} */
259 #endif /* MYSQLND_COMPRESSION_ENABLED */
260 
261 
262 /* {{{ mysqlnd_pfc::decode */
263 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,decode)264 MYSQLND_METHOD(mysqlnd_pfc, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
265 									const zend_uchar * const compressed_data, const size_t compressed_data_len)
266 {
267 #ifdef MYSQLND_COMPRESSION_ENABLED
268 	int error;
269 	uLongf tmp_complen = uncompressed_data_len;
270 	DBG_ENTER("mysqlnd_pfc::decode");
271 	error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
272 
273 	DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
274 	if (error != Z_OK) {
275 		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);
276 	}
277 	DBG_RETURN(error == Z_OK? PASS:FAIL);
278 #else
279 	DBG_ENTER("mysqlnd_pfc::decode");
280 	DBG_RETURN(FAIL);
281 #endif
282 }
283 /* }}} */
284 
285 
286 /* {{{ mysqlnd_pfc::encode */
287 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,encode)288 MYSQLND_METHOD(mysqlnd_pfc, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
289 									const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len)
290 {
291 #ifdef MYSQLND_COMPRESSION_ENABLED
292 	int error;
293 	uLongf tmp_complen = *compress_buffer_len;
294 	DBG_ENTER("mysqlnd_pfc::encode");
295 	error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
296 
297 	if (error != Z_OK) {
298 		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);
299 	} else {
300 		*compress_buffer_len = tmp_complen;
301 		DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
302 	}
303 
304 	DBG_RETURN(error == Z_OK? PASS:FAIL);
305 #else
306 	DBG_ENTER("mysqlnd_pfc::encode");
307 	DBG_RETURN(FAIL);
308 #endif
309 }
310 /* }}} */
311 
312 
313 /* {{{ mysqlnd_pfc::receive */
314 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,receive)315 MYSQLND_METHOD(mysqlnd_pfc, receive)(MYSQLND_PFC * const pfc, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
316 									 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
317 {
318 	size_t to_read = count;
319 	zend_uchar * p = buffer;
320 
321 	DBG_ENTER("mysqlnd_pfc::receive");
322 #ifdef MYSQLND_COMPRESSION_ENABLED
323 	if (pfc->data->compressed) {
324 		if (pfc->data->uncompressed_data) {
325 			size_t to_read_from_buffer = MIN(pfc->data->uncompressed_data->bytes_left(pfc->data->uncompressed_data), to_read);
326 			DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer);
327 			if (to_read_from_buffer) {
328 				pfc->data->uncompressed_data->read(pfc->data->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
329 				p += to_read_from_buffer;
330 				to_read -= to_read_from_buffer;
331 			}
332 			DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read);
333 			if (TRUE == pfc->data->uncompressed_data->is_empty(pfc->data->uncompressed_data)) {
334 				/* Everything was consumed. This should never happen here, but for security */
335 				pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
336 			}
337 		}
338 		if (to_read) {
339 			zend_uchar net_header[MYSQLND_HEADER_SIZE];
340 			size_t net_payload_size;
341 			zend_uchar packet_no;
342 
343 			if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
344 				DBG_RETURN(FAIL);
345 			}
346 			net_payload_size = uint3korr(net_header);
347 			packet_no = uint1korr(net_header + 3);
348 			if (pfc->data->compressed_envelope_packet_no != packet_no) {
349 				DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
350 							pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
351 
352 				php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
353 						  pfc->data->compressed_envelope_packet_no, packet_no, net_payload_size);
354 				DBG_RETURN(FAIL);
355 			}
356 			pfc->data->compressed_envelope_packet_no++;
357 #ifdef MYSQLND_DUMP_HEADER_N_BODY
358 			DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size);
359 #endif
360 			/* Now let's read from the wire, decompress it and fill the read buffer */
361 			pfc->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(pfc, vio, net_payload_size, conn_stats, error_info);
362 
363 			/*
364 			  Now a bit of recursion - read from the read buffer,
365 			  if the data which we have just read from the wire
366 			  is not enough, then the recursive call will try to
367 			  satisfy it until it is satisfied.
368 			*/
369 			DBG_RETURN(pfc->data->m.receive(pfc, vio, p, to_read, conn_stats, error_info));
370 		}
371 		DBG_RETURN(PASS);
372 	}
373 #endif /* MYSQLND_COMPRESSION_ENABLED */
374 	DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info));
375 }
376 /* }}} */
377 
378 
379 /* {{{ mysqlnd_pfc::set_client_option */
380 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,set_client_option)381 MYSQLND_METHOD(mysqlnd_pfc, set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value)
382 {
383 	DBG_ENTER("mysqlnd_pfc::set_client_option");
384 	DBG_INF_FMT("option=%u", option);
385 	switch (option) {
386 		case MYSQL_OPT_COMPRESS:
387 			pfc->data->flags |= MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION;
388 			break;
389 		case MYSQL_SERVER_PUBLIC_KEY: {
390 			const zend_bool pers = pfc->persistent;
391 			if (pfc->data->sha256_server_public_key) {
392 				mnd_pefree(pfc->data->sha256_server_public_key, pers);
393 			}
394 			pfc->data->sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
395 			break;
396 		}
397 		case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: {
398 			DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
399 			if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
400 				DBG_RETURN(FAIL);
401 			}
402 			pfc->cmd_buffer.length = *(unsigned int*) value;
403 			DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, pfc->cmd_buffer.length);
404 			if (!pfc->cmd_buffer.buffer) {
405 				pfc->cmd_buffer.buffer = mnd_pemalloc(pfc->cmd_buffer.length, pfc->persistent);
406 			} else {
407 				pfc->cmd_buffer.buffer = mnd_perealloc(pfc->cmd_buffer.buffer, pfc->cmd_buffer.length, pfc->persistent);
408 			}
409 			break;
410 		}
411 		default:
412 			DBG_RETURN(FAIL);
413 	}
414 	DBG_RETURN(PASS);
415 }
416 /* }}} */
417 
418 
419 /* {{{ mysqlnd_pfc::free_contents */
420 static void
MYSQLND_METHOD(mysqlnd_pfc,free_contents)421 MYSQLND_METHOD(mysqlnd_pfc, free_contents)(MYSQLND_PFC * pfc)
422 {
423 	DBG_ENTER("mysqlnd_pfc::free_contents");
424 
425 #ifdef MYSQLND_COMPRESSION_ENABLED
426 	if (pfc->data->uncompressed_data) {
427 		pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
428 	}
429 #endif
430 	if (pfc->data->sha256_server_public_key) {
431 		mnd_pefree(pfc->data->sha256_server_public_key, pfc->persistent);
432 		pfc->data->sha256_server_public_key = NULL;
433 	}
434 
435 	DBG_VOID_RETURN;
436 }
437 /* }}} */
438 
439 
440 /* {{{ mysqlnd_pfc::init */
441 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,init)442 MYSQLND_METHOD(mysqlnd_pfc, init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
443 {
444 	unsigned int buf_size;
445 	DBG_ENTER("mysqlnd_pfc::init");
446 
447 	buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
448 	pfc->data->m.set_client_option(pfc, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
449 
450 	DBG_RETURN(PASS);
451 }
452 /* }}} */
453 
454 
455 /* {{{ mysqlnd_pfc::dtor */
456 static void
MYSQLND_METHOD(mysqlnd_pfc,dtor)457 MYSQLND_METHOD(mysqlnd_pfc, dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
458 {
459 	DBG_ENTER("mysqlnd_pfc::dtor");
460 	if (pfc) {
461 		pfc->data->m.free_contents(pfc);
462 
463 		if (pfc->cmd_buffer.buffer) {
464 			DBG_INF("Freeing cmd buffer");
465 			mnd_pefree(pfc->cmd_buffer.buffer, pfc->persistent);
466 			pfc->cmd_buffer.buffer = NULL;
467 		}
468 
469 		mnd_pefree(pfc, pfc->persistent);
470 	}
471 	DBG_VOID_RETURN;
472 }
473 /* }}} */
474 
475 
476 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_packet_frame_codec)
477 	MYSQLND_METHOD(mysqlnd_pfc, init),
478 	MYSQLND_METHOD(mysqlnd_pfc, dtor),
479 	MYSQLND_METHOD(mysqlnd_pfc, reset),
480 
481 	MYSQLND_METHOD(mysqlnd_pfc, set_client_option),
482 
483 	MYSQLND_METHOD(mysqlnd_pfc, decode),
484 	MYSQLND_METHOD(mysqlnd_pfc, encode),
485 
486 	MYSQLND_METHOD(mysqlnd_pfc, send),
487 	MYSQLND_METHOD(mysqlnd_pfc, receive),
488 
489 #ifdef MYSQLND_COMPRESSION_ENABLED
490 	MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer),
491 #else
492 	NULL,
493 #endif
494 
495 	MYSQLND_METHOD(mysqlnd_pfc, free_contents),
496 MYSQLND_CLASS_METHODS_END;
497 
498 
499 /* {{{ mysqlnd_pfc_init */
500 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)501 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 {
503 	MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
504 	MYSQLND_PFC * pfc;
505 	DBG_ENTER("mysqlnd_pfc_init");
506 	pfc = factory->get_protocol_frame_codec(persistent, stats, error_info);
507 	DBG_RETURN(pfc);
508 }
509 /* }}} */
510 
511 
512 /* {{{ mysqlnd_pfc_free */
513 PHPAPI void
mysqlnd_pfc_free(MYSQLND_PFC * const pfc,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)514 mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
515 {
516 	DBG_ENTER("mysqlnd_pfc_free");
517 	if (pfc) {
518 		pfc->data->m.dtor(pfc, stats, error_info);
519 	}
520 	DBG_VOID_RETURN;
521 }
522 /* }}} */
523