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 net->data->options.timeout_read = *(unsigned int*) value;
822 break;
823 #ifdef WHEN_SUPPORTED_BY_MYSQLI
824 case MYSQL_OPT_WRITE_TIMEOUT:
825 net->data->options.timeout_write = *(unsigned int*) value;
826 break;
827 #endif
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 if (!context) {
910 DBG_RETURN(FAIL);
911 }
912
913 if (net->data->options.ssl_key) {
914 zval key_zval;
915 ZVAL_STRING(&key_zval, net->data->options.ssl_key);
916 php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
917 zval_ptr_dtor(&key_zval);
918 any_flag = TRUE;
919 }
920 if (net->data->options.ssl_cert) {
921 zval cert_zval;
922 ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
923 php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
924 if (!net->data->options.ssl_key) {
925 php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
926 }
927 zval_ptr_dtor(&cert_zval);
928 any_flag = TRUE;
929 }
930 if (net->data->options.ssl_ca) {
931 zval cafile_zval;
932 ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
933 php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
934 any_flag = TRUE;
935 }
936 if (net->data->options.ssl_capath) {
937 zval capath_zval;
938 ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
939 php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
940 zval_ptr_dtor(&capath_zval);
941 any_flag = TRUE;
942 }
943 if (net->data->options.ssl_passphrase) {
944 zval passphrase_zval;
945 ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
946 php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
947 zval_ptr_dtor(&passphrase_zval);
948 any_flag = TRUE;
949 }
950 if (net->data->options.ssl_cipher) {
951 zval cipher_zval;
952 ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
953 php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
954 zval_ptr_dtor(&cipher_zval);
955 any_flag = TRUE;
956 }
957 {
958 zval verify_peer_zval;
959 zend_bool verify;
960
961 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
962 net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
963 }
964
965 verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
966
967 DBG_INF_FMT("VERIFY=%d", verify);
968 ZVAL_BOOL(&verify_peer_zval, verify);
969 php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
970 php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
971 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
972 ZVAL_TRUE(&verify_peer_zval);
973 php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
974 }
975 }
976 php_stream_context_set(net_stream, context);
977 if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
978 php_stream_xport_crypto_enable(net_stream, 1) < 0)
979 {
980 DBG_ERR("Cannot connect to MySQL by using SSL");
981 php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
982 DBG_RETURN(FAIL);
983 }
984 net->data->ssl = TRUE;
985 /*
986 get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
987 then the context would not survive cleaning of EG(regular_list), where it is registered, as a
988 resource. What happens is that after this destruction any use of the network will mean usage
989 of the context, which means usage of already freed memory, bad. Actually we don't need this
990 context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
991 */
992 php_stream_context_set(net_stream, NULL);
993
994 if (net->data->options.timeout_read) {
995 struct timeval tv;
996 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
997 tv.tv_sec = net->data->options.timeout_read;
998 tv.tv_usec = 0;
999 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
1000 }
1001
1002 DBG_RETURN(PASS);
1003 #else
1004 DBG_ENTER("mysqlnd_net::enable_ssl");
1005 DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
1006 DBG_RETURN(PASS);
1007 #endif
1008 }
1009 /* }}} */
1010
1011
1012 /* {{{ mysqlnd_net::disable_ssl */
1013 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,disable_ssl)1014 MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net)
1015 {
1016 DBG_ENTER("mysqlnd_net::disable_ssl");
1017 DBG_RETURN(PASS);
1018 }
1019 /* }}} */
1020
1021
1022 /* {{{ mysqlnd_net::free_contents */
1023 static void
MYSQLND_METHOD(mysqlnd_net,free_contents)1024 MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net)
1025 {
1026 zend_bool pers = net->persistent;
1027 DBG_ENTER("mysqlnd_net::free_contents");
1028
1029 #ifdef MYSQLND_COMPRESSION_ENABLED
1030 if (net->uncompressed_data) {
1031 net->uncompressed_data->free_buffer(&net->uncompressed_data);
1032 }
1033 #endif
1034 if (net->data->options.ssl_key) {
1035 mnd_pefree(net->data->options.ssl_key, pers);
1036 net->data->options.ssl_key = NULL;
1037 }
1038 if (net->data->options.ssl_cert) {
1039 mnd_pefree(net->data->options.ssl_cert, pers);
1040 net->data->options.ssl_cert = NULL;
1041 }
1042 if (net->data->options.ssl_ca) {
1043 mnd_pefree(net->data->options.ssl_ca, pers);
1044 net->data->options.ssl_ca = NULL;
1045 }
1046 if (net->data->options.ssl_capath) {
1047 mnd_pefree(net->data->options.ssl_capath, pers);
1048 net->data->options.ssl_capath = NULL;
1049 }
1050 if (net->data->options.ssl_cipher) {
1051 mnd_pefree(net->data->options.ssl_cipher, pers);
1052 net->data->options.ssl_cipher = NULL;
1053 }
1054 if (net->data->options.sha256_server_public_key) {
1055 mnd_pefree(net->data->options.sha256_server_public_key, pers);
1056 net->data->options.sha256_server_public_key = NULL;
1057 }
1058
1059 DBG_VOID_RETURN;
1060 }
1061 /* }}} */
1062
1063
1064 /* {{{ mysqlnd_net::close_stream */
1065 static void
MYSQLND_METHOD(mysqlnd_net,close_stream)1066 MYSQLND_METHOD(mysqlnd_net, close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
1067 {
1068 php_stream * net_stream;
1069 DBG_ENTER("mysqlnd_net::close_stream");
1070 if (net && (net_stream = net->data->m.get_stream(net))) {
1071 zend_bool pers = net->persistent;
1072 DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
1073 if (pers) {
1074 if (EG(active)) {
1075 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
1076 } else {
1077 /*
1078 otherwise we will crash because the EG(persistent_list) has been freed already,
1079 before the modules are shut down
1080 */
1081 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
1082 }
1083 } else {
1084 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
1085 }
1086 (void) net->data->m.set_stream(net, NULL);
1087 }
1088
1089 DBG_VOID_RETURN;
1090 }
1091 /* }}} */
1092
1093
1094 /* {{{ mysqlnd_net::init */
1095 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,init)1096 MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
1097 {
1098 unsigned int buf_size;
1099 DBG_ENTER("mysqlnd_net::init");
1100
1101 buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
1102 net->data->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size);
1103
1104 buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
1105 net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
1106
1107 buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
1108 net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
1109
1110 DBG_RETURN(PASS);
1111 }
1112 /* }}} */
1113
1114
1115 /* {{{ mysqlnd_net::dtor */
1116 static void
MYSQLND_METHOD(mysqlnd_net,dtor)1117 MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
1118 {
1119 DBG_ENTER("mysqlnd_net::dtor");
1120 if (net) {
1121 net->data->m.free_contents(net);
1122 net->data->m.close_stream(net, stats, error_info);
1123
1124 if (net->cmd_buffer.buffer) {
1125 DBG_INF("Freeing cmd buffer");
1126 mnd_pefree(net->cmd_buffer.buffer, net->persistent);
1127 net->cmd_buffer.buffer = NULL;
1128 }
1129
1130 mnd_pefree(net->data, net->data->persistent);
1131 mnd_pefree(net, net->persistent);
1132 }
1133 DBG_VOID_RETURN;
1134 }
1135 /* }}} */
1136
1137
1138 /* {{{ mysqlnd_net::get_stream */
1139 static php_stream *
MYSQLND_METHOD(mysqlnd_net,get_stream)1140 MYSQLND_METHOD(mysqlnd_net, get_stream)(const MYSQLND_NET * const net)
1141 {
1142 DBG_ENTER("mysqlnd_net::get_stream");
1143 DBG_INF_FMT("%p", net? net->data->stream:NULL);
1144 DBG_RETURN(net? net->data->stream:NULL);
1145 }
1146 /* }}} */
1147
1148
1149 /* {{{ mysqlnd_net::set_stream */
1150 static php_stream *
MYSQLND_METHOD(mysqlnd_net,set_stream)1151 MYSQLND_METHOD(mysqlnd_net, set_stream)(MYSQLND_NET * const net, php_stream * net_stream)
1152 {
1153 php_stream * ret = NULL;
1154 DBG_ENTER("mysqlnd_net::set_stream");
1155 if (net) {
1156 net->data->stream = net_stream;
1157 ret = net->data->stream;
1158 }
1159 DBG_RETURN(ret);
1160 }
1161 /* }}} */
1162
1163
1164 MYSQLND_CLASS_METHODS_START(mysqlnd_net)
1165 MYSQLND_METHOD(mysqlnd_net, init),
1166 MYSQLND_METHOD(mysqlnd_net, dtor),
1167 MYSQLND_METHOD(mysqlnd_net, connect_ex),
1168 MYSQLND_METHOD(mysqlnd_net, close_stream),
1169 MYSQLND_METHOD(mysqlnd_net, open_pipe),
1170 MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix),
1171 MYSQLND_METHOD(mysqlnd_net, get_stream),
1172 MYSQLND_METHOD(mysqlnd_net, set_stream),
1173 MYSQLND_METHOD(mysqlnd_net, get_open_stream),
1174 MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt),
1175 MYSQLND_METHOD(mysqlnd_net, set_client_option),
1176 MYSQLND_METHOD(mysqlnd_net, decode),
1177 MYSQLND_METHOD(mysqlnd_net, encode),
1178 MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data),
1179 MYSQLND_METHOD(mysqlnd_net, free_contents),
1180 MYSQLND_METHOD(mysqlnd_net, enable_ssl),
1181 MYSQLND_METHOD(mysqlnd_net, disable_ssl),
1182 MYSQLND_METHOD(mysqlnd_net, network_read_ex),
1183 MYSQLND_METHOD(mysqlnd_net, network_write_ex),
1184 MYSQLND_METHOD(mysqlnd_net, send_ex),
1185 MYSQLND_METHOD(mysqlnd_net, receive_ex),
1186 #ifdef MYSQLND_COMPRESSION_ENABLED
1187 MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer),
1188 #else
1189 NULL,
1190 #endif
1191 NULL, /* unused 1 */
1192 NULL, /* unused 2 */
1193 NULL, /* unused 3 */
1194 NULL, /* unused 4 */
1195 NULL /* unused 5 */
1196 MYSQLND_CLASS_METHODS_END;
1197
1198
1199 /* {{{ mysqlnd_net_init */
1200 PHPAPI MYSQLND_NET *
mysqlnd_net_init(zend_bool persistent,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)1201 mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
1202 {
1203 MYSQLND_NET * net;
1204 DBG_ENTER("mysqlnd_net_init");
1205 net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_io_channel(persistent, stats, error_info);
1206 DBG_RETURN(net);
1207 }
1208 /* }}} */
1209
1210
1211 /* {{{ mysqlnd_net_free */
1212 PHPAPI void
mysqlnd_net_free(MYSQLND_NET * const net,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)1213 mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
1214 {
1215 DBG_ENTER("mysqlnd_net_free");
1216 if (net) {
1217 net->data->m.dtor(net, stats, error_info);
1218 }
1219 DBG_VOID_RETURN;
1220 }
1221 /* }}} */
1222
1223
1224
1225 /*
1226 * Local variables:
1227 * tab-width: 4
1228 * c-basic-offset: 4
1229 * End:
1230 * vim600: noet sw=4 ts=4 fdm=marker
1231 * vim<600: noet sw=4 ts=4
1232 */
1233