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