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 "php_globals.h"
22 #include "mysqlnd.h"
23 #include "mysqlnd_priv.h"
24 #include "mysqlnd_wireprotocol.h"
25 #include "mysqlnd_statistics.h"
26 #include "mysqlnd_debug.h"
27 #include "mysqlnd_ext_plugin.h"
28 #include "php_network.h"
29 #include "zend_ini.h"
30 #ifdef MYSQLND_COMPRESSION_ENABLED
31 #include <zlib.h>
32 #endif
33
34 #ifndef PHP_WIN32
35 #include <netinet/tcp.h>
36 #else
37 #include <winsock.h>
38 #endif
39
40
41 /* {{{ mysqlnd_set_sock_no_delay */
42 static int
mysqlnd_set_sock_no_delay(php_stream * stream)43 mysqlnd_set_sock_no_delay(php_stream * stream)
44 {
45
46 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
47 int ret = SUCCESS;
48 int flag = 1;
49 int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
50
51 DBG_ENTER("mysqlnd_set_sock_no_delay");
52
53 if (result == -1) {
54 ret = FAILURE;
55 }
56
57 DBG_RETURN(ret);
58 }
59 /* }}} */
60
61
62 /* {{{ mysqlnd_set_sock_keepalive */
63 static int
mysqlnd_set_sock_keepalive(php_stream * stream)64 mysqlnd_set_sock_keepalive(php_stream * stream)
65 {
66
67 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
68 int ret = SUCCESS;
69 int flag = 1;
70 int result = setsockopt(socketd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int));
71
72 DBG_ENTER("mysqlnd_set_sock_keepalive");
73
74 if (result == -1) {
75 ret = FAILURE;
76 }
77
78 DBG_RETURN(ret);
79 }
80 /* }}} */
81
82
83 /* {{{ mysqlnd_net::network_read_ex */
84 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,network_read_ex)85 MYSQLND_METHOD(mysqlnd_net, network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
86 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
87 {
88 enum_func_status return_value = PASS;
89 php_stream * net_stream = net->data->m.get_stream(net);
90 size_t old_chunk_size = net_stream->chunk_size;
91 size_t to_read = count, ret;
92 zend_uchar * p = buffer;
93
94 DBG_ENTER("mysqlnd_net::network_read_ex");
95 DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
96
97 net_stream->chunk_size = MIN(to_read, net->data->options.net_read_buffer_size);
98 while (to_read) {
99 if (!(ret = php_stream_read(net_stream, (char *) p, to_read))) {
100 DBG_ERR_FMT("Error while reading header from socket");
101 return_value = FAIL;
102 break;
103 }
104 p += ret;
105 to_read -= ret;
106 }
107 MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
108 net_stream->chunk_size = old_chunk_size;
109 DBG_RETURN(return_value);
110 }
111 /* }}} */
112
113
114 /* {{{ mysqlnd_net::network_write_ex */
115 static size_t
MYSQLND_METHOD(mysqlnd_net,network_write_ex)116 MYSQLND_METHOD(mysqlnd_net, network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buffer, const size_t count,
117 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
118 {
119 size_t ret;
120 DBG_ENTER("mysqlnd_net::network_write_ex");
121 DBG_INF_FMT("sending %u bytes", count);
122 ret = php_stream_write(net->data->m.get_stream(net), (char *)buffer, count);
123 DBG_RETURN(ret);
124 }
125 /* }}} */
126
127
128 /* {{{ mysqlnd_net::open_pipe */
129 static php_stream *
MYSQLND_METHOD(mysqlnd_net,open_pipe)130 MYSQLND_METHOD(mysqlnd_net, open_pipe)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
131 const zend_bool persistent,
132 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
133 {
134 unsigned int streams_options = 0;
135 dtor_func_t origin_dtor;
136 php_stream * net_stream = NULL;
137
138 DBG_ENTER("mysqlnd_net::open_pipe");
139 if (persistent) {
140 streams_options |= STREAM_OPEN_PERSISTENT;
141 }
142 streams_options |= IGNORE_URL;
143 net_stream = php_stream_open_wrapper((char*) scheme + sizeof("pipe://") - 1, "r+", streams_options, NULL);
144 if (!net_stream) {
145 SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
146 DBG_RETURN(NULL);
147 }
148 /*
149 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
150 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
151 unregistered until the script ends. So, we need to take care of that.
152 */
153 origin_dtor = EG(regular_list).pDestructor;
154 EG(regular_list).pDestructor = NULL;
155 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
156 EG(regular_list).pDestructor = origin_dtor;
157 net_stream->res = NULL;
158
159 DBG_RETURN(net_stream);
160 }
161 /* }}} */
162
163
164 /* {{{ mysqlnd_net::open_tcp_or_unix */
165 static php_stream *
MYSQLND_METHOD(mysqlnd_net,open_tcp_or_unix)166 MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
167 const zend_bool persistent,
168 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
169 {
170 unsigned int streams_options = 0;
171 unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
172 char * hashed_details = NULL;
173 int hashed_details_len = 0;
174 zend_string *errstr = NULL;
175 int errcode = 0;
176 struct timeval tv;
177 dtor_func_t origin_dtor;
178 php_stream * net_stream = NULL;
179
180 DBG_ENTER("mysqlnd_net::open_tcp_or_unix");
181
182 net->data->stream = NULL;
183
184 if (persistent) {
185 hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", net);
186 DBG_INF_FMT("hashed_details=%s", hashed_details);
187 }
188
189 if (net->data->options.timeout_connect) {
190 tv.tv_sec = net->data->options.timeout_connect;
191 tv.tv_usec = 0;
192 }
193
194 DBG_INF_FMT("calling php_stream_xport_create");
195 net_stream = php_stream_xport_create(scheme, scheme_len, streams_options, streams_flags,
196 hashed_details, (net->data->options.timeout_connect) ? &tv : NULL,
197 NULL /*ctx*/, &errstr, &errcode);
198 if (errstr || !net_stream) {
199 DBG_ERR("Error");
200 if (hashed_details) {
201 mnd_sprintf_free(hashed_details);
202 }
203 errcode = CR_CONNECTION_ERROR;
204 SET_CLIENT_ERROR(*error_info,
205 CR_CONNECTION_ERROR,
206 UNKNOWN_SQLSTATE,
207 errstr? ZSTR_VAL(errstr):"Unknown error while connecting");
208 if (errstr) {
209 zend_string_release(errstr);
210 }
211 DBG_RETURN(NULL);
212 }
213 if (hashed_details) {
214 /*
215 If persistent, the streams register it in EG(persistent_list).
216 This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
217 whatever they have to.
218 */
219 zend_resource *le;
220
221 if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
222 origin_dtor = EG(persistent_list).pDestructor;
223 /*
224 in_free will let streams code skip destructing - big HACK,
225 but STREAMS suck big time regarding persistent streams.
226 Just not compatible for extensions that need persistency.
227 */
228 EG(persistent_list).pDestructor = NULL;
229 zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
230 EG(persistent_list).pDestructor = origin_dtor;
231 pefree(le, 1);
232 }
233 #if ZEND_DEBUG
234 /* Shut-up the streams, they don't know what they are doing */
235 net_stream->__exposed = 1;
236 #endif
237 mnd_sprintf_free(hashed_details);
238 }
239
240 /*
241 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
242 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
243 unregistered until the script ends. So, we need to take care of that.
244 */
245 origin_dtor = EG(regular_list).pDestructor;
246 EG(regular_list).pDestructor = NULL;
247 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
248 efree(net_stream->res);
249 net_stream->res = NULL;
250 EG(regular_list).pDestructor = origin_dtor;
251 DBG_RETURN(net_stream);
252 }
253 /* }}} */
254
255
256 /* {{{ mysqlnd_net::post_connect_set_opt */
257 static void
MYSQLND_METHOD(mysqlnd_net,post_connect_set_opt)258 MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt)(MYSQLND_NET * const net,
259 const char * const scheme, const size_t scheme_len,
260 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
261 {
262 php_stream * net_stream = net->data->m.get_stream(net);
263 DBG_ENTER("mysqlnd_net::post_connect_set_opt");
264 if (net_stream) {
265 if (net->data->options.timeout_read) {
266 struct timeval tv;
267 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
268 tv.tv_sec = net->data->options.timeout_read;
269 tv.tv_usec = 0;
270 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
271 }
272
273 if (!memcmp(scheme, "tcp://", sizeof("tcp://") - 1)) {
274 /* TCP -> Set TCP_NODELAY */
275 mysqlnd_set_sock_no_delay(net_stream);
276 /* TCP -> Set SO_KEEPALIVE */
277 mysqlnd_set_sock_keepalive(net_stream);
278 }
279 }
280
281 DBG_VOID_RETURN;
282 }
283 /* }}} */
284
285
286 /* {{{ mysqlnd_net::get_open_stream */
287 static func_mysqlnd_net__open_stream
MYSQLND_METHOD(mysqlnd_net,get_open_stream)288 MYSQLND_METHOD(mysqlnd_net, get_open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
289 MYSQLND_ERROR_INFO * const error_info)
290 {
291 func_mysqlnd_net__open_stream ret = NULL;
292 DBG_ENTER("mysqlnd_net::get_open_stream");
293 if (scheme_len > (sizeof("pipe://") - 1) && !memcmp(scheme, "pipe://", sizeof("pipe://") - 1)) {
294 ret = net->data->m.open_pipe;
295 } else if ((scheme_len > (sizeof("tcp://") - 1) && !memcmp(scheme, "tcp://", sizeof("tcp://") - 1))
296 ||
297 (scheme_len > (sizeof("unix://") - 1) && !memcmp(scheme, "unix://", sizeof("unix://") - 1)))
298 {
299 ret = net->data->m.open_tcp_or_unix;
300 }
301
302 if (!ret) {
303 SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "No handler for this scheme");
304 }
305
306 DBG_RETURN(ret);
307 }
308 /* }}} */
309
310
311 /* {{{ mysqlnd_net::connect_ex */
312 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,connect_ex)313 MYSQLND_METHOD(mysqlnd_net, connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
314 const zend_bool persistent,
315 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
316 {
317 enum_func_status ret = FAIL;
318 func_mysqlnd_net__open_stream open_stream = NULL;
319 DBG_ENTER("mysqlnd_net::connect_ex");
320
321 net->packet_no = net->compressed_envelope_packet_no = 0;
322
323 net->data->m.close_stream(net, conn_stats, error_info);
324
325 open_stream = net->data->m.get_open_stream(net, scheme, scheme_len, error_info);
326 if (open_stream) {
327 php_stream * net_stream = open_stream(net, scheme, scheme_len, persistent, conn_stats, error_info);
328 if (net_stream) {
329 (void) net->data->m.set_stream(net, net_stream);
330 net->data->m.post_connect_set_opt(net, scheme, scheme_len, conn_stats, error_info);
331 ret = PASS;
332 }
333 }
334
335 DBG_RETURN(ret);
336 }
337 /* }}} */
338
339
340 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
341 #define COPY_HEADER(T,A) do { \
342 *(((char *)(T))) = *(((char *)(A)));\
343 *(((char *)(T))+1) = *(((char *)(A))+1);\
344 *(((char *)(T))+2) = *(((char *)(A))+2);\
345 *(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
346 #define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer))
347 #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
348
349
350 /* {{{ mysqlnd_net::send_ex */
351 /*
352 IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
353 This is done for performance reasons in the caller of this function.
354 Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
355 Neither are quick, thus the clients of this function are obligated to do
356 what they are asked for.
357
358 `count` is actually the length of the payload data. Thus :
359 count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
360 */
361 static size_t
MYSQLND_METHOD(mysqlnd_net,send_ex)362 MYSQLND_METHOD(mysqlnd_net, send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
363 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
364 {
365 zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
366 zend_uchar * safe_storage = safe_buf;
367 size_t bytes_sent, packets_sent = 1;
368 size_t left = count;
369 zend_uchar * p = (zend_uchar *) buffer;
370 zend_uchar * compress_buf = NULL;
371 size_t to_be_sent;
372
373 DBG_ENTER("mysqlnd_net::send_ex");
374 DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->data->compressed);
375
376 if (net->data->compressed == TRUE) {
377 size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
378 DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
379 compress_buf = mnd_emalloc(comp_buf_size);
380 }
381
382 do {
383 to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
384 DBG_INF_FMT("to_be_sent=%u", to_be_sent);
385 DBG_INF_FMT("packets_sent=%u", packets_sent);
386 DBG_INF_FMT("compressed_envelope_packet_no=%u", net->compressed_envelope_packet_no);
387 DBG_INF_FMT("packet_no=%u", net->packet_no);
388 #ifdef MYSQLND_COMPRESSION_ENABLED
389 if (net->data->compressed == TRUE) {
390 /* here we need to compress the data and then write it, first comes the compressed header */
391 size_t tmp_complen = to_be_sent;
392 size_t payload_size;
393 zend_uchar * uncompressed_payload = p; /* should include the header */
394
395 STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
396 int3store(uncompressed_payload, to_be_sent);
397 int1store(uncompressed_payload + 3, net->packet_no);
398 if (PASS == net->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
399 uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE))
400 {
401 int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
402 payload_size = tmp_complen;
403 } else {
404 int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
405 memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
406 payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
407 }
408 RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
409
410 int3store(compress_buf, payload_size);
411 int1store(compress_buf + 3, net->packet_no);
412 DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
413 bytes_sent = net->data->m.network_write_ex(net, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE,
414 conn_stats, error_info);
415 net->compressed_envelope_packet_no++;
416 #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
417 if (res == Z_OK) {
418 size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
419 zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
420 int error = net->data->m.decode(decompressed_data, decompressed_size,
421 compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
422 if (error == Z_OK) {
423 int i;
424 DBG_INF("success decompressing");
425 for (i = 0 ; i < decompressed_size; i++) {
426 if (i && (i % 30 == 0)) {
427 printf("\n\t\t");
428 }
429 printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
430 DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
431 }
432 } else {
433 DBG_INF("error decompressing");
434 }
435 mnd_free(decompressed_data);
436 }
437 #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
438 } else
439 #endif /* MYSQLND_COMPRESSION_ENABLED */
440 {
441 DBG_INF("no compression");
442 STORE_HEADER_SIZE(safe_storage, p);
443 int3store(p, to_be_sent);
444 int1store(p + 3, net->packet_no);
445 bytes_sent = net->data->m.network_write_ex(net, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info);
446 RESTORE_HEADER_SIZE(p, safe_storage);
447 net->compressed_envelope_packet_no++;
448 }
449 net->packet_no++;
450
451 p += to_be_sent;
452 left -= to_be_sent;
453 packets_sent++;
454 /*
455 if left is 0 then there is nothing more to send, but if the last packet was exactly
456 with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
457 empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
458 indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
459 packet will be sent and this loop will end.
460 */
461 } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
462
463 DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no);
464
465 MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
466 STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
467 STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
468 STAT_PACKETS_SENT, packets_sent);
469
470 if (compress_buf) {
471 mnd_efree(compress_buf);
472 }
473
474 /* Even for zero size payload we have to send a packet */
475 if (!bytes_sent) {
476 DBG_ERR_FMT("Can't %u send bytes", count);
477 SET_CLIENT_ERROR(*error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
478 }
479 DBG_RETURN(bytes_sent);
480 }
481 /* }}} */
482
483
484 #ifdef MYSQLND_COMPRESSION_ENABLED
485 /* {{{ php_mysqlnd_read_buffer_is_empty */
486 static zend_bool
php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)487 php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
488 {
489 return buffer->len? FALSE:TRUE;
490 }
491 /* }}} */
492
493
494 /* {{{ php_mysqlnd_read_buffer_read */
495 static void
php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer,size_t count,zend_uchar * dest)496 php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
497 {
498 if (buffer->len >= count) {
499 memcpy(dest, buffer->data + buffer->offset, count);
500 buffer->offset += count;
501 buffer->len -= count;
502 }
503 }
504 /* }}} */
505
506
507 /* {{{ php_mysqlnd_read_buffer_bytes_left */
508 static size_t
php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)509 php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
510 {
511 return buffer->len;
512 }
513 /* }}} */
514
515
516 /* {{{ php_mysqlnd_read_buffer_free */
517 static void
php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer)518 php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer)
519 {
520 DBG_ENTER("php_mysqlnd_read_buffer_free");
521 if (*buffer) {
522 mnd_efree((*buffer)->data);
523 mnd_efree(*buffer);
524 *buffer = NULL;
525 }
526 DBG_VOID_RETURN;
527 }
528 /* }}} */
529
530
531 /* {{{ php_mysqlnd_create_read_buffer */
532 static MYSQLND_READ_BUFFER *
mysqlnd_create_read_buffer(size_t count)533 mysqlnd_create_read_buffer(size_t count)
534 {
535 MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
536 DBG_ENTER("mysqlnd_create_read_buffer");
537 ret->is_empty = php_mysqlnd_read_buffer_is_empty;
538 ret->read = php_mysqlnd_read_buffer_read;
539 ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
540 ret->free_buffer = php_mysqlnd_read_buffer_free;
541 ret->data = mnd_emalloc(count);
542 ret->size = ret->len = count;
543 ret->offset = 0;
544 DBG_RETURN(ret);
545 }
546 /* }}} */
547
548
549 /* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */
550 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,read_compressed_packet_from_stream_and_fill_read_buffer)551 MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer)
552 (MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info)
553 {
554 size_t decompressed_size;
555 enum_func_status retval = PASS;
556 zend_uchar * compressed_data = NULL;
557 zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
558 DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer");
559
560 /* Read the compressed header */
561 if (FAIL == net->data->m.network_read_ex(net, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) {
562 DBG_RETURN(FAIL);
563 }
564 decompressed_size = uint3korr(comp_header);
565
566 /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
567 /* we need to decompress the data */
568
569 if (decompressed_size) {
570 compressed_data = mnd_emalloc(net_payload_size);
571 if (FAIL == net->data->m.network_read_ex(net, compressed_data, net_payload_size, conn_stats, error_info)) {
572 retval = FAIL;
573 goto end;
574 }
575 net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size);
576 retval = net->data->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size);
577 if (FAIL == retval) {
578 goto end;
579 }
580 } else {
581 DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
582 net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size);
583 if (FAIL == net->data->m.network_read_ex(net, net->uncompressed_data->data, net_payload_size, conn_stats, error_info)) {
584 retval = FAIL;
585 goto end;
586 }
587 }
588 end:
589 if (compressed_data) {
590 mnd_efree(compressed_data);
591 }
592 DBG_RETURN(retval);
593 }
594 /* }}} */
595 #endif /* MYSQLND_COMPRESSION_ENABLED */
596
597
598 /* {{{ mysqlnd_net::decode */
599 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,decode)600 MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
601 const zend_uchar * const compressed_data, const size_t compressed_data_len)
602 {
603 #ifdef MYSQLND_COMPRESSION_ENABLED
604 int error;
605 uLongf tmp_complen = uncompressed_data_len;
606 DBG_ENTER("mysqlnd_net::decode");
607 error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
608
609 DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
610 if (error != Z_OK) {
611 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);
612 }
613 DBG_RETURN(error == Z_OK? PASS:FAIL);
614 #else
615 DBG_ENTER("mysqlnd_net::decode");
616 DBG_RETURN(FAIL);
617 #endif
618 }
619 /* }}} */
620
621
622 /* {{{ mysqlnd_net::encode */
623 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,encode)624 MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
625 const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len)
626 {
627 #ifdef MYSQLND_COMPRESSION_ENABLED
628 int error;
629 uLongf tmp_complen = *compress_buffer_len;
630 DBG_ENTER("mysqlnd_net::encode");
631 error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
632
633 if (error != Z_OK) {
634 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);
635 } else {
636 *compress_buffer_len = tmp_complen;
637 DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
638 }
639
640 DBG_RETURN(error == Z_OK? PASS:FAIL);
641 #else
642 DBG_ENTER("mysqlnd_net::encode");
643 DBG_RETURN(FAIL);
644 #endif
645 }
646 /* }}} */
647
648
649 /* {{{ mysqlnd_net::receive_ex */
650 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,receive_ex)651 MYSQLND_METHOD(mysqlnd_net, receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
652 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
653 {
654 size_t to_read = count;
655 zend_uchar * p = buffer;
656
657 DBG_ENTER("mysqlnd_net::receive_ex");
658 #ifdef MYSQLND_COMPRESSION_ENABLED
659 if (net->data->compressed) {
660 if (net->uncompressed_data) {
661 size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
662 DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer);
663 if (to_read_from_buffer) {
664 net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
665 p += to_read_from_buffer;
666 to_read -= to_read_from_buffer;
667 }
668 DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read);
669 if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
670 /* Everything was consumed. This should never happen here, but for security */
671 net->uncompressed_data->free_buffer(&net->uncompressed_data);
672 }
673 }
674 if (to_read) {
675 zend_uchar net_header[MYSQLND_HEADER_SIZE];
676 size_t net_payload_size;
677 zend_uchar packet_no;
678
679 if (FAIL == net->data->m.network_read_ex(net, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) {
680 DBG_RETURN(FAIL);
681 }
682 net_payload_size = uint3korr(net_header);
683 packet_no = uint1korr(net_header + 3);
684 if (net->compressed_envelope_packet_no != packet_no) {
685 DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
686 net->compressed_envelope_packet_no, packet_no, net_payload_size);
687
688 php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
689 net->compressed_envelope_packet_no, packet_no, net_payload_size);
690 DBG_RETURN(FAIL);
691 }
692 net->compressed_envelope_packet_no++;
693 #ifdef MYSQLND_DUMP_HEADER_N_BODY
694 DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size);
695 #endif
696 /* Now let's read from the wire, decompress it and fill the read buffer */
697 net->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(net, net_payload_size, conn_stats, error_info);
698
699 /*
700 Now a bit of recursion - read from the read buffer,
701 if the data which we have just read from the wire
702 is not enough, then the recursive call will try to
703 satisfy it until it is satisfied.
704 */
705 DBG_RETURN(net->data->m.receive_ex(net, p, to_read, conn_stats, error_info));
706 }
707 DBG_RETURN(PASS);
708 }
709 #endif /* MYSQLND_COMPRESSION_ENABLED */
710 DBG_RETURN(net->data->m.network_read_ex(net, p, to_read, conn_stats, error_info));
711 }
712 /* }}} */
713
714
715 /* {{{ mysqlnd_net::set_client_option */
716 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,set_client_option)717 MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mysqlnd_option option, const char * const value)
718 {
719 DBG_ENTER("mysqlnd_net::set_client_option");
720 DBG_INF_FMT("option=%u", option);
721 switch (option) {
722 case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
723 DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
724 if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
725 DBG_RETURN(FAIL);
726 }
727 net->cmd_buffer.length = *(unsigned int*) value;
728 DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->cmd_buffer.length);
729 if (!net->cmd_buffer.buffer) {
730 net->cmd_buffer.buffer = mnd_pemalloc(net->cmd_buffer.length, net->persistent);
731 } else {
732 net->cmd_buffer.buffer = mnd_perealloc(net->cmd_buffer.buffer, net->cmd_buffer.length, net->persistent);
733 }
734 break;
735 case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
736 DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
737 net->data->options.net_read_buffer_size = *(unsigned int*) value;
738 DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->data->options.net_read_buffer_size);
739 break;
740 case MYSQL_OPT_CONNECT_TIMEOUT:
741 DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
742 net->data->options.timeout_connect = *(unsigned int*) value;
743 break;
744 case MYSQLND_OPT_SSL_KEY:
745 {
746 zend_bool pers = net->persistent;
747 if (net->data->options.ssl_key) {
748 mnd_pefree(net->data->options.ssl_key, pers);
749 }
750 net->data->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
751 break;
752 }
753 case MYSQLND_OPT_SSL_CERT:
754 {
755 zend_bool pers = net->persistent;
756 if (net->data->options.ssl_cert) {
757 mnd_pefree(net->data->options.ssl_cert, pers);
758 }
759 net->data->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
760 break;
761 }
762 case MYSQLND_OPT_SSL_CA:
763 {
764 zend_bool pers = net->persistent;
765 if (net->data->options.ssl_ca) {
766 mnd_pefree(net->data->options.ssl_ca, pers);
767 }
768 net->data->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
769 break;
770 }
771 case MYSQLND_OPT_SSL_CAPATH:
772 {
773 zend_bool pers = net->persistent;
774 if (net->data->options.ssl_capath) {
775 mnd_pefree(net->data->options.ssl_capath, pers);
776 }
777 net->data->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
778 break;
779 }
780 case MYSQLND_OPT_SSL_CIPHER:
781 {
782 zend_bool pers = net->persistent;
783 if (net->data->options.ssl_cipher) {
784 mnd_pefree(net->data->options.ssl_cipher, pers);
785 }
786 net->data->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
787 break;
788 }
789 case MYSQLND_OPT_SSL_PASSPHRASE:
790 {
791 zend_bool pers = net->persistent;
792 if (net->data->options.ssl_passphrase) {
793 mnd_pefree(net->data->options.ssl_passphrase, pers);
794 }
795 net->data->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
796 break;
797 }
798 case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
799 {
800 enum mysqlnd_ssl_peer val = *((enum mysqlnd_ssl_peer *)value);
801 switch (val) {
802 case MYSQLND_SSL_PEER_VERIFY:
803 DBG_INF("MYSQLND_SSL_PEER_VERIFY");
804 break;
805 case MYSQLND_SSL_PEER_DONT_VERIFY:
806 DBG_INF("MYSQLND_SSL_PEER_DONT_VERIFY");
807 break;
808 case MYSQLND_SSL_PEER_DEFAULT:
809 DBG_INF("MYSQLND_SSL_PEER_DEFAULT");
810 val = MYSQLND_SSL_PEER_DEFAULT;
811 break;
812 default:
813 DBG_INF("default = MYSQLND_SSL_PEER_DEFAULT_ACTION");
814 val = MYSQLND_SSL_PEER_DEFAULT;
815 break;
816 }
817 net->data->options.ssl_verify_peer = val;
818 break;
819 }
820 case MYSQL_OPT_READ_TIMEOUT:
821 DBG_INF("MYSQL_OPT_READ_TIMEOUT");
822 net->data->options.timeout_read = *(unsigned int*) value;
823 break;
824 case MYSQL_OPT_WRITE_TIMEOUT:
825 DBG_INF("MYSQL_OPT_WRITE_TIMEOUT");
826 net->data->options.timeout_write = *(unsigned int*) value;
827 break;
828 case MYSQL_OPT_COMPRESS:
829 net->data->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION;
830 break;
831 case MYSQL_SERVER_PUBLIC_KEY:
832 {
833 zend_bool pers = net->persistent;
834 if (net->data->options.sha256_server_public_key) {
835 mnd_pefree(net->data->options.sha256_server_public_key, pers);
836 }
837 net->data->options.sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
838 break;
839 }
840 default:
841 DBG_RETURN(FAIL);
842 }
843 DBG_RETURN(PASS);
844 }
845 /* }}} */
846
847 /* {{{ mysqlnd_net::consume_uneaten_data */
848 size_t
MYSQLND_METHOD(mysqlnd_net,consume_uneaten_data)849 MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd)
850 {
851 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
852 /*
853 Switch to non-blocking mode and try to consume something from
854 the line, if possible, then continue. This saves us from looking for
855 the actual place where out-of-order packets have been sent.
856 If someone is completely sure that everything is fine, he can switch it
857 off.
858 */
859 char tmp_buf[256];
860 size_t skipped_bytes = 0;
861 int opt = PHP_STREAM_OPTION_BLOCKING;
862 php_stream * net_stream = net->data->get_stream(net);
863 int was_blocked = net_stream->ops->set_option(net_stream, opt, 0, NULL);
864
865 DBG_ENTER("mysqlnd_net::consume_uneaten_data");
866
867 if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
868 /* Do a read of 1 byte */
869 int bytes_consumed;
870
871 do {
872 skipped_bytes += (bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf)));
873 } while (bytes_consumed == sizeof(tmp_buf));
874
875 if (was_blocked) {
876 net_stream->ops->set_option(net_stream, opt, 1, NULL);
877 }
878
879 if (bytes_consumed) {
880 DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
881 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
882 php_error_docref(NULL, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
883 "consumed all the output from the server",
884 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
885 }
886 }
887 net->last_command = cmd;
888
889 DBG_RETURN(skipped_bytes);
890 #else
891 return 0;
892 #endif
893 }
894 /* }}} */
895
896 /*
897 in libmyusql, if cert and !key then key=cert
898 */
899 /* {{{ mysqlnd_net::enable_ssl */
900 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,enable_ssl)901 MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net)
902 {
903 #ifdef MYSQLND_SSL_SUPPORTED
904 php_stream_context * context = php_stream_context_alloc();
905 php_stream * net_stream = net->data->m.get_stream(net);
906 zend_bool any_flag = FALSE;
907
908 DBG_ENTER("mysqlnd_net::enable_ssl");
909
910 if (net->data->options.ssl_key) {
911 zval key_zval;
912 ZVAL_STRING(&key_zval, net->data->options.ssl_key);
913 php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
914 zval_ptr_dtor(&key_zval);
915 any_flag = TRUE;
916 }
917 if (net->data->options.ssl_cert) {
918 zval cert_zval;
919 ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
920 php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
921 if (!net->data->options.ssl_key) {
922 php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
923 }
924 zval_ptr_dtor(&cert_zval);
925 any_flag = TRUE;
926 }
927 if (net->data->options.ssl_ca) {
928 zval cafile_zval;
929 ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
930 php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
931 any_flag = TRUE;
932 }
933 if (net->data->options.ssl_capath) {
934 zval capath_zval;
935 ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
936 php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
937 zval_ptr_dtor(&capath_zval);
938 any_flag = TRUE;
939 }
940 if (net->data->options.ssl_passphrase) {
941 zval passphrase_zval;
942 ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
943 php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
944 zval_ptr_dtor(&passphrase_zval);
945 any_flag = TRUE;
946 }
947 if (net->data->options.ssl_cipher) {
948 zval cipher_zval;
949 ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
950 php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
951 zval_ptr_dtor(&cipher_zval);
952 any_flag = TRUE;
953 }
954 {
955 zval verify_peer_zval;
956 zend_bool verify;
957
958 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
959 net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
960 }
961
962 verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
963
964 DBG_INF_FMT("VERIFY=%d", verify);
965 ZVAL_BOOL(&verify_peer_zval, verify);
966 php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
967 php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
968 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
969 ZVAL_TRUE(&verify_peer_zval);
970 php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
971 }
972 }
973 php_stream_context_set(net_stream, context);
974 if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
975 php_stream_xport_crypto_enable(net_stream, 1) < 0)
976 {
977 DBG_ERR("Cannot connect to MySQL by using SSL");
978 php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
979 DBG_RETURN(FAIL);
980 }
981 net->data->ssl = TRUE;
982 /*
983 get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
984 then the context would not survive cleaning of EG(regular_list), where it is registered, as a
985 resource. What happens is that after this destruction any use of the network will mean usage
986 of the context, which means usage of already freed memory, bad. Actually we don't need this
987 context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
988 */
989 php_stream_context_set(net_stream, NULL);
990
991 if (net->data->options.timeout_read) {
992 struct timeval tv;
993 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
994 tv.tv_sec = net->data->options.timeout_read;
995 tv.tv_usec = 0;
996 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
997 }
998
999 DBG_RETURN(PASS);
1000 #else
1001 DBG_ENTER("mysqlnd_net::enable_ssl");
1002 DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
1003 DBG_RETURN(PASS);
1004 #endif
1005 }
1006 /* }}} */
1007
1008
1009 /* {{{ mysqlnd_net::disable_ssl */
1010 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,disable_ssl)1011 MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net)
1012 {
1013 DBG_ENTER("mysqlnd_net::disable_ssl");
1014 DBG_RETURN(PASS);
1015 }
1016 /* }}} */
1017
1018
1019 /* {{{ mysqlnd_net::free_contents */
1020 static void
MYSQLND_METHOD(mysqlnd_net,free_contents)1021 MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net)
1022 {
1023 zend_bool pers = net->persistent;
1024 DBG_ENTER("mysqlnd_net::free_contents");
1025
1026 #ifdef MYSQLND_COMPRESSION_ENABLED
1027 if (net->uncompressed_data) {
1028 net->uncompressed_data->free_buffer(&net->uncompressed_data);
1029 }
1030 #endif
1031 if (net->data->options.ssl_key) {
1032 mnd_pefree(net->data->options.ssl_key, pers);
1033 net->data->options.ssl_key = NULL;
1034 }
1035 if (net->data->options.ssl_cert) {
1036 mnd_pefree(net->data->options.ssl_cert, pers);
1037 net->data->options.ssl_cert = NULL;
1038 }
1039 if (net->data->options.ssl_ca) {
1040 mnd_pefree(net->data->options.ssl_ca, pers);
1041 net->data->options.ssl_ca = NULL;
1042 }
1043 if (net->data->options.ssl_capath) {
1044 mnd_pefree(net->data->options.ssl_capath, pers);
1045 net->data->options.ssl_capath = NULL;
1046 }
1047 if (net->data->options.ssl_cipher) {
1048 mnd_pefree(net->data->options.ssl_cipher, pers);
1049 net->data->options.ssl_cipher = NULL;
1050 }
1051 if (net->data->options.sha256_server_public_key) {
1052 mnd_pefree(net->data->options.sha256_server_public_key, pers);
1053 net->data->options.sha256_server_public_key = NULL;
1054 }
1055
1056 DBG_VOID_RETURN;
1057 }
1058 /* }}} */
1059
1060
1061 /* {{{ mysqlnd_net::close_stream */
1062 static void
MYSQLND_METHOD(mysqlnd_net,close_stream)1063 MYSQLND_METHOD(mysqlnd_net, close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
1064 {
1065 php_stream * net_stream;
1066 DBG_ENTER("mysqlnd_net::close_stream");
1067 if (net && (net_stream = net->data->m.get_stream(net))) {
1068 zend_bool pers = net->persistent;
1069 DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
1070 if (pers) {
1071 if (EG(active)) {
1072 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
1073 } else {
1074 /*
1075 otherwise we will crash because the EG(persistent_list) has been freed already,
1076 before the modules are shut down
1077 */
1078 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
1079 }
1080 } else {
1081 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
1082 }
1083 (void) net->data->m.set_stream(net, NULL);
1084 }
1085
1086 DBG_VOID_RETURN;
1087 }
1088 /* }}} */
1089
1090
1091 /* {{{ mysqlnd_net::init */
1092 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,init)1093 MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
1094 {
1095 unsigned int buf_size;
1096 DBG_ENTER("mysqlnd_net::init");
1097
1098 buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
1099 net->data->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
1100
1101 buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
1102 net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
1103
1104 buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
1105 net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
1106
1107 DBG_RETURN(PASS);
1108 }
1109 /* }}} */
1110
1111
1112 /* {{{ mysqlnd_net::dtor */
1113 static void
MYSQLND_METHOD(mysqlnd_net,dtor)1114 MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
1115 {
1116 DBG_ENTER("mysqlnd_net::dtor");
1117 if (net) {
1118 net->data->m.free_contents(net);
1119 net->data->m.close_stream(net, stats, error_info);
1120
1121 if (net->cmd_buffer.buffer) {
1122 DBG_INF("Freeing cmd buffer");
1123 mnd_pefree(net->cmd_buffer.buffer, net->persistent);
1124 net->cmd_buffer.buffer = NULL;
1125 }
1126
1127 mnd_pefree(net->data, net->data->persistent);
1128 mnd_pefree(net, net->persistent);
1129 }
1130 DBG_VOID_RETURN;
1131 }
1132 /* }}} */
1133
1134
1135 /* {{{ mysqlnd_net::get_stream */
1136 static php_stream *
MYSQLND_METHOD(mysqlnd_net,get_stream)1137 MYSQLND_METHOD(mysqlnd_net, get_stream)(const MYSQLND_NET * const net)
1138 {
1139 DBG_ENTER("mysqlnd_net::get_stream");
1140 DBG_INF_FMT("%p", net? net->data->stream:NULL);
1141 DBG_RETURN(net? net->data->stream:NULL);
1142 }
1143 /* }}} */
1144
1145
1146 /* {{{ mysqlnd_net::set_stream */
1147 static php_stream *
MYSQLND_METHOD(mysqlnd_net,set_stream)1148 MYSQLND_METHOD(mysqlnd_net, set_stream)(MYSQLND_NET * const net, php_stream * net_stream)
1149 {
1150 php_stream * ret = NULL;
1151 DBG_ENTER("mysqlnd_net::set_stream");
1152 if (net) {
1153 net->data->stream = net_stream;
1154 ret = net->data->stream;
1155 }
1156 DBG_RETURN(ret);
1157 }
1158 /* }}} */
1159
1160
1161 MYSQLND_CLASS_METHODS_START(mysqlnd_net)
1162 MYSQLND_METHOD(mysqlnd_net, init),
1163 MYSQLND_METHOD(mysqlnd_net, dtor),
1164 MYSQLND_METHOD(mysqlnd_net, connect_ex),
1165 MYSQLND_METHOD(mysqlnd_net, close_stream),
1166 MYSQLND_METHOD(mysqlnd_net, open_pipe),
1167 MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix),
1168 MYSQLND_METHOD(mysqlnd_net, get_stream),
1169 MYSQLND_METHOD(mysqlnd_net, set_stream),
1170 MYSQLND_METHOD(mysqlnd_net, get_open_stream),
1171 MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt),
1172 MYSQLND_METHOD(mysqlnd_net, set_client_option),
1173 MYSQLND_METHOD(mysqlnd_net, decode),
1174 MYSQLND_METHOD(mysqlnd_net, encode),
1175 MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data),
1176 MYSQLND_METHOD(mysqlnd_net, free_contents),
1177 MYSQLND_METHOD(mysqlnd_net, enable_ssl),
1178 MYSQLND_METHOD(mysqlnd_net, disable_ssl),
1179 MYSQLND_METHOD(mysqlnd_net, network_read_ex),
1180 MYSQLND_METHOD(mysqlnd_net, network_write_ex),
1181 MYSQLND_METHOD(mysqlnd_net, send_ex),
1182 MYSQLND_METHOD(mysqlnd_net, receive_ex),
1183 #ifdef MYSQLND_COMPRESSION_ENABLED
1184 MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer),
1185 #else
1186 NULL,
1187 #endif
1188 NULL, /* unused 1 */
1189 NULL, /* unused 2 */
1190 NULL, /* unused 3 */
1191 NULL, /* unused 4 */
1192 NULL /* unused 5 */
1193 MYSQLND_CLASS_METHODS_END;
1194
1195
1196 /* {{{ mysqlnd_net_init */
1197 PHPAPI MYSQLND_NET *
mysqlnd_net_init(zend_bool persistent,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)1198 mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
1199 {
1200 MYSQLND_NET * net;
1201 DBG_ENTER("mysqlnd_net_init");
1202 net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_io_channel(persistent, stats, error_info);
1203 DBG_RETURN(net);
1204 }
1205 /* }}} */
1206
1207
1208 /* {{{ mysqlnd_net_free */
1209 PHPAPI void
mysqlnd_net_free(MYSQLND_NET * const net,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)1210 mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
1211 {
1212 DBG_ENTER("mysqlnd_net_free");
1213 if (net) {
1214 net->data->m.dtor(net, stats, error_info);
1215 }
1216 DBG_VOID_RETURN;
1217 }
1218 /* }}} */
1219
1220
1221
1222 /*
1223 * Local variables:
1224 * tab-width: 4
1225 * c-basic-offset: 4
1226 * End:
1227 * vim600: noet sw=4 ts=4 fdm=marker
1228 * vim<600: noet sw=4 ts=4
1229 */
1230