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