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 /* Now let's read from the wire, decompress it and fill the read buffer */
359 pfc->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(pfc, vio, net_payload_size, conn_stats, error_info);
360
361 /*
362 Now a bit of recursion - read from the read buffer,
363 if the data which we have just read from the wire
364 is not enough, then the recursive call will try to
365 satisfy it until it is satisfied.
366 */
367 DBG_RETURN(pfc->data->m.receive(pfc, vio, p, to_read, conn_stats, error_info));
368 }
369 DBG_RETURN(PASS);
370 }
371 #endif /* MYSQLND_COMPRESSION_ENABLED */
372 DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info));
373 }
374 /* }}} */
375
376
377 /* {{{ mysqlnd_pfc::set_client_option */
378 static enum_func_status
MYSQLND_METHOD(mysqlnd_pfc,set_client_option)379 MYSQLND_METHOD(mysqlnd_pfc, set_client_option)(MYSQLND_PFC * const pfc, enum_mysqlnd_client_option option, const char * const value)
380 {
381 DBG_ENTER("mysqlnd_pfc::set_client_option");
382 DBG_INF_FMT("option=%u", option);
383 switch (option) {
384 case MYSQL_OPT_COMPRESS:
385 pfc->data->flags |= MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION;
386 break;
387 case MYSQL_SERVER_PUBLIC_KEY: {
388 const bool pers = pfc->persistent;
389 if (pfc->data->sha256_server_public_key) {
390 mnd_pefree(pfc->data->sha256_server_public_key, pers);
391 }
392 pfc->data->sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
393 break;
394 }
395 case MYSQLND_OPT_NET_CMD_BUFFER_SIZE: {
396 DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
397 if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
398 DBG_RETURN(FAIL);
399 }
400 pfc->cmd_buffer.length = *(unsigned int*) value;
401 DBG_INF_FMT("new_length=%zu", pfc->cmd_buffer.length);
402 if (!pfc->cmd_buffer.buffer) {
403 pfc->cmd_buffer.buffer = mnd_pemalloc(pfc->cmd_buffer.length, pfc->persistent);
404 } else {
405 pfc->cmd_buffer.buffer = mnd_perealloc(pfc->cmd_buffer.buffer, pfc->cmd_buffer.length, pfc->persistent);
406 }
407 break;
408 }
409 default:
410 DBG_RETURN(FAIL);
411 }
412 DBG_RETURN(PASS);
413 }
414 /* }}} */
415
416
417 /* {{{ mysqlnd_pfc::free_contents */
418 static void
MYSQLND_METHOD(mysqlnd_pfc,free_contents)419 MYSQLND_METHOD(mysqlnd_pfc, free_contents)(MYSQLND_PFC * pfc)
420 {
421 DBG_ENTER("mysqlnd_pfc::free_contents");
422
423 #ifdef MYSQLND_COMPRESSION_ENABLED
424 if (pfc->data->uncompressed_data) {
425 pfc->data->uncompressed_data->free_buffer(&pfc->data->uncompressed_data);
426 }
427 #endif
428 if (pfc->data->sha256_server_public_key) {
429 mnd_pefree(pfc->data->sha256_server_public_key, pfc->persistent);
430 pfc->data->sha256_server_public_key = NULL;
431 }
432
433 DBG_VOID_RETURN;
434 }
435 /* }}} */
436
437
438 /* {{{ mysqlnd_pfc::init */
439 static void
MYSQLND_METHOD(mysqlnd_pfc,init)440 MYSQLND_METHOD(mysqlnd_pfc, init)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
441 {
442 unsigned int buf_size;
443 DBG_ENTER("mysqlnd_pfc::init");
444
445 buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
446 pfc->data->m.set_client_option(pfc, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
447
448 DBG_VOID_RETURN;
449 }
450 /* }}} */
451
452
453 /* {{{ mysqlnd_pfc::dtor */
454 static void
MYSQLND_METHOD(mysqlnd_pfc,dtor)455 MYSQLND_METHOD(mysqlnd_pfc, dtor)(MYSQLND_PFC * const pfc, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
456 {
457 DBG_ENTER("mysqlnd_pfc::dtor");
458 if (pfc) {
459 pfc->data->m.free_contents(pfc);
460
461 if (pfc->cmd_buffer.buffer) {
462 DBG_INF("Freeing cmd buffer");
463 mnd_pefree(pfc->cmd_buffer.buffer, pfc->persistent);
464 pfc->cmd_buffer.buffer = NULL;
465 }
466
467 mnd_pefree(pfc, pfc->persistent);
468 }
469 DBG_VOID_RETURN;
470 }
471 /* }}} */
472
473
474 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_packet_frame_codec)
475 MYSQLND_METHOD(mysqlnd_pfc, init),
476 MYSQLND_METHOD(mysqlnd_pfc, dtor),
477 MYSQLND_METHOD(mysqlnd_pfc, reset),
478
479 MYSQLND_METHOD(mysqlnd_pfc, set_client_option),
480
481 MYSQLND_METHOD(mysqlnd_pfc, decode),
482 MYSQLND_METHOD(mysqlnd_pfc, encode),
483
484 MYSQLND_METHOD(mysqlnd_pfc, send),
485 MYSQLND_METHOD(mysqlnd_pfc, receive),
486
487 #ifdef MYSQLND_COMPRESSION_ENABLED
488 MYSQLND_METHOD(mysqlnd_pfc, read_compressed_packet_from_stream_and_fill_read_buffer),
489 #else
490 NULL,
491 #endif
492
493 MYSQLND_METHOD(mysqlnd_pfc, free_contents),
494 MYSQLND_CLASS_METHODS_END;
495
496
497 /* {{{ mysqlnd_pfc_init */
498 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)499 mysqlnd_pfc_init(const bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
500 {
501 MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
502 MYSQLND_PFC * pfc;
503 DBG_ENTER("mysqlnd_pfc_init");
504 pfc = factory->get_protocol_frame_codec(persistent, stats, error_info);
505 DBG_RETURN(pfc);
506 }
507 /* }}} */
508
509
510 /* {{{ mysqlnd_pfc_free */
511 PHPAPI void
mysqlnd_pfc_free(MYSQLND_PFC * const pfc,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)512 mysqlnd_pfc_free(MYSQLND_PFC * const pfc, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
513 {
514 DBG_ENTER("mysqlnd_pfc_free");
515 if (pfc) {
516 pfc->data->m.dtor(pfc, stats, error_info);
517 }
518 DBG_VOID_RETURN;
519 }
520 /* }}} */
521