2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2013 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@mysql.com> |
16 | Ulf Wendel <uwendel@mysql.com> |
17 | Georg Richter <georg@mysql.com> |
18 +----------------------------------------------------------------------+
21 /* $Id: mysqlnd_ps.c 316906 2011-09-17 10:24:18Z pajoye $ */
23 #include "php_globals.h"
25 #include "mysqlnd_priv.h"
26 #include "mysqlnd_wireprotocol.h"
27 #include "mysqlnd_statistics.h"
28 #include "mysqlnd_debug.h"
29 #include "mysqlnd_ext_plugin.h"
30 #include "php_network.h"
32 #ifdef MYSQLND_COMPRESSION_ENABLED
37 #include <netinet/tcp.h>
43 /* {{{ mysqlnd_set_sock_no_delay */
45 mysqlnd_set_sock_no_delay(php_stream
* stream TSRMLS_DC
)
48 int socketd
= ((php_netstream_data_t
*)stream
->abstract
)->socket
;
51 int result
= setsockopt(socketd
, IPPROTO_TCP
, TCP_NODELAY
, (char *) &flag
, sizeof(int));
53 DBG_ENTER("mysqlnd_set_sock_no_delay");
64 /* {{{ mysqlnd_net::network_read_ex */
65 static enum_func_status
66 MYSQLND_METHOD(mysqlnd_net
, network_read_ex
)(MYSQLND_NET
* const net
, zend_uchar
* const buffer
, const size_t count
,
67 MYSQLND_STATS
* const stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
69 enum_func_status return_value
= PASS
;
70 size_t to_read
= count
, ret
;
71 size_t old_chunk_size
= net
->stream
->chunk_size
;
72 zend_uchar
* p
= buffer
;
74 DBG_ENTER("mysqlnd_net::network_read_ex");
75 DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC
, count
);
77 net
->stream
->chunk_size
= MIN(to_read
, net
->options
.net_read_buffer_size
);
79 if (!(ret
= php_stream_read(net
->stream
, (char *) p
, to_read
))) {
80 DBG_ERR_FMT("Error while reading header from socket");
87 MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats
, STAT_BYTES_RECEIVED
, count
- to_read
);
88 net
->stream
->chunk_size
= old_chunk_size
;
89 DBG_RETURN(return_value
);
94 /* {{{ mysqlnd_net::network_write_ex */
96 MYSQLND_METHOD(mysqlnd_net
, network_write_ex
)(MYSQLND_NET
* const net
, const zend_uchar
* const buffer
, const size_t count
,
97 MYSQLND_STATS
* const stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
100 DBG_ENTER("mysqlnd_net::network_write_ex");
101 ret
= php_stream_write(net
->stream
, (char *)buffer
, count
);
106 /* {{{ mysqlnd_net::open_pipe */
107 static enum_func_status
108 MYSQLND_METHOD(mysqlnd_net
, open_pipe
)(MYSQLND_NET
* const net
, const char * const scheme
, const size_t scheme_len
,
109 const zend_bool persistent
,
110 MYSQLND_STATS
* const conn_stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
112 #if PHP_API_VERSION < 20100412
113 unsigned int streams_options
= ENFORCE_SAFE_MODE
;
115 unsigned int streams_options
= 0;
117 DBG_ENTER("mysqlnd_net::open_pipe");
119 streams_options
|= STREAM_OPEN_PERSISTENT
;
121 streams_options
|= IGNORE_URL
;
122 net
->stream
= php_stream_open_wrapper((char*) scheme
+ sizeof("pipe://") - 1, "r+", streams_options
, NULL
);
124 SET_CLIENT_ERROR(*error_info
, CR_CONNECTION_ERROR
, UNKNOWN_SQLSTATE
, "Unknown errror while connecting");
128 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
129 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
130 unregistered yntil the script ends. So, we need to take care of that.
132 net
->stream
->in_free
= 1;
133 zend_hash_index_del(&EG(regular_list
), net
->stream
->rsrc_id
);
134 net
->stream
->in_free
= 0;
141 /* {{{ mysqlnd_net::open_tcp_or_unix */
142 static enum_func_status
143 MYSQLND_METHOD(mysqlnd_net
, open_tcp_or_unix
)(MYSQLND_NET
* const net
, const char * const scheme
, const size_t scheme_len
,
144 const zend_bool persistent
,
145 MYSQLND_STATS
* const conn_stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
147 #if PHP_API_VERSION < 20100412
148 unsigned int streams_options
= ENFORCE_SAFE_MODE
;
150 unsigned int streams_options
= 0;
152 unsigned int streams_flags
= STREAM_XPORT_CLIENT
| STREAM_XPORT_CONNECT
;
153 char * hashed_details
= NULL
;
154 int hashed_details_len
= 0;
155 char * errstr
= NULL
;
159 DBG_ENTER("mysqlnd_net::open_tcp_or_unix");
162 hashed_details_len
= mnd_sprintf(&hashed_details
, 0, "%p", net
);
163 DBG_INF_FMT("hashed_details=%s", hashed_details
);
166 if (net
->options
.timeout_connect
) {
167 tv
.tv_sec
= net
->options
.timeout_connect
;
171 DBG_INF_FMT("calling php_stream_xport_create");
172 net
->stream
= php_stream_xport_create(scheme
, scheme_len
, streams_options
, streams_flags
,
173 hashed_details
, (net
->options
.timeout_connect
) ? &tv
: NULL
,
174 NULL
/*ctx*/, &errstr
, &errcode
);
175 if (errstr
|| !net
->stream
) {
177 if (hashed_details
) {
178 mnd_sprintf_free(hashed_details
);
180 errcode
= CR_CONNECTION_ERROR
;
181 SET_CLIENT_ERROR(*error_info
, errcode
? errcode
:CR_CONNECTION_ERROR
, UNKNOWN_SQLSTATE
, errstr
);
183 /* no mnd_ since we don't allocate it */
188 if (hashed_details
) {
190 If persistent, the streams register it in EG(persistent_list).
191 This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
192 whatever they have to.
194 zend_rsrc_list_entry
*le
;
196 if (zend_hash_find(&EG(persistent_list
), hashed_details
, hashed_details_len
+ 1, (void*) &le
) == SUCCESS
) {
198 in_free will let streams code skip destructing - big HACK,
199 but STREAMS suck big time regarding persistent streams.
200 Just not compatible for extensions that need persistency.
202 net
->stream
->in_free
= 1;
203 zend_hash_del(&EG(persistent_list
), hashed_details
, hashed_details_len
+ 1);
204 net
->stream
->in_free
= 0;
207 /* Shut-up the streams, they don't know what they are doing */
208 net
->stream
->__exposed
= 1;
210 mnd_sprintf_free(hashed_details
);
214 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
215 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
216 unregistered yntil the script ends. So, we need to take care of that.
218 net
->stream
->in_free
= 1;
219 zend_hash_index_del(&EG(regular_list
), net
->stream
->rsrc_id
);
220 net
->stream
->in_free
= 0;
227 /* {{{ mysqlnd_net::connect_ex */
229 MYSQLND_METHOD(mysqlnd_net
, post_connect_set_opt
)(MYSQLND_NET
* const net
,
230 const char * const scheme
, const size_t scheme_len
,
231 MYSQLND_STATS
* const conn_stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
233 DBG_ENTER("mysqlnd_net::post_connect_set_opt");
234 if (net
->options
.timeout_read
) {
236 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net
->options
.timeout_read
);
237 tv
.tv_sec
= net
->options
.timeout_read
;
239 php_stream_set_option(net
->stream
, PHP_STREAM_OPTION_READ_TIMEOUT
, 0, &tv
);
242 if (!memcmp(scheme
, "tcp://", sizeof("tcp://") - 1)) {
243 /* TCP -> Set TCP_NODELAY */
244 mysqlnd_set_sock_no_delay(net
->stream TSRMLS_CC
);
252 /* {{{ mysqlnd_net::connect_ex */
253 static enum_func_status
254 MYSQLND_METHOD(mysqlnd_net
, connect_ex
)(MYSQLND_NET
* const net
, const char * const scheme
, const size_t scheme_len
,
255 const zend_bool persistent
,
256 MYSQLND_STATS
* const conn_stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
258 enum_func_status ret
= FAIL
;
259 func_mysqlnd_net__open_stream open_stream
= NULL
;
260 DBG_ENTER("mysqlnd_net::connect_ex");
262 net
->packet_no
= net
->compressed_envelope_packet_no
= 0;
264 net
->m
.close_stream(net
, conn_stats
, error_info TSRMLS_CC
);
266 open_stream
= (scheme_len
> (sizeof("pipe://") - 1) && !memcmp(scheme
, "pipe://", sizeof("pipe://") - 1))? net
->m
.open_pipe
:
267 net
->m
.open_tcp_or_unix
;
269 if (PASS
== (ret
= open_stream(net
, scheme
, scheme_len
, persistent
, conn_stats
, error_info TSRMLS_CC
))) {
270 net
->m
.post_connect_set_opt(net
, scheme
, scheme_len
, conn_stats
, error_info TSRMLS_CC
);
278 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
279 #define COPY_HEADER(T,A) do { \
280 *(((char *)(T))) = *(((char *)(A)));\
281 *(((char *)(T))+1) = *(((char *)(A))+1);\
282 *(((char *)(T))+2) = *(((char *)(A))+2);\
283 *(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
284 #define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer))
285 #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
288 /* {{{ mysqlnd_net::send_ex */
290 IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
291 This is done for performance reasons in the caller of this function.
292 Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
293 Neither are quick, thus the clients of this function are obligated to do
294 what they are asked for.
296 `count` is actually the length of the payload data. Thus :
297 count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
300 MYSQLND_METHOD(mysqlnd_net
, send_ex
)(MYSQLND_NET
* const net
, zend_uchar
* const buffer
, const size_t count
,
301 MYSQLND_STATS
* const conn_stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
303 zend_uchar safe_buf
[((MYSQLND_HEADER_SIZE
) + (sizeof(zend_uchar
)) - 1) / (sizeof(zend_uchar
))];
304 zend_uchar
* safe_storage
= safe_buf
;
305 size_t bytes_sent
, packets_sent
= 1;
307 zend_uchar
* p
= (zend_uchar
*) buffer
;
308 zend_uchar
* compress_buf
= NULL
;
311 DBG_ENTER("mysqlnd_net::send_ex");
312 DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC
" compression=%u", count
, net
->compressed
);
314 if (net
->compressed
== TRUE
) {
315 size_t comp_buf_size
= MYSQLND_HEADER_SIZE
+ COMPRESSED_HEADER_SIZE
+ MYSQLND_HEADER_SIZE
+ MIN(left
, MYSQLND_MAX_PACKET_SIZE
);
316 DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC
, comp_buf_size
);
317 compress_buf
= mnd_emalloc(comp_buf_size
);
321 to_be_sent
= MIN(left
, MYSQLND_MAX_PACKET_SIZE
);
322 #ifdef MYSQLND_COMPRESSION_ENABLED
323 if (net
->compressed
== TRUE
) {
324 /* here we need to compress the data and then write it, first comes the compressed header */
325 size_t tmp_complen
= to_be_sent
;
327 zend_uchar
* uncompressed_payload
= p
; /* should include the header */
329 STORE_HEADER_SIZE(safe_storage
, uncompressed_payload
);
330 int3store(uncompressed_payload
, to_be_sent
);
331 int1store(uncompressed_payload
+ 3, net
->packet_no
);
332 if (PASS
== net
->m
.encode((compress_buf
+ COMPRESSED_HEADER_SIZE
+ MYSQLND_HEADER_SIZE
), &tmp_complen
,
333 uncompressed_payload
, to_be_sent
+ MYSQLND_HEADER_SIZE TSRMLS_CC
))
335 int3store(compress_buf
+ MYSQLND_HEADER_SIZE
, to_be_sent
+ MYSQLND_HEADER_SIZE
);
336 payload_size
= tmp_complen
;
338 int3store(compress_buf
+ MYSQLND_HEADER_SIZE
, 0);
339 memcpy(compress_buf
+ MYSQLND_HEADER_SIZE
+ COMPRESSED_HEADER_SIZE
, uncompressed_payload
, to_be_sent
+ MYSQLND_HEADER_SIZE
);
340 payload_size
= to_be_sent
+ MYSQLND_HEADER_SIZE
;
342 RESTORE_HEADER_SIZE(uncompressed_payload
, safe_storage
);
344 int3store(compress_buf
, payload_size
);
345 int1store(compress_buf
+ 3, net
->packet_no
);
346 DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC
" bytes to the network", payload_size
+ MYSQLND_HEADER_SIZE
+ COMPRESSED_HEADER_SIZE
);
347 bytes_sent
= net
->m
.network_write_ex(net
, compress_buf
, payload_size
+ MYSQLND_HEADER_SIZE
+ COMPRESSED_HEADER_SIZE
,
348 conn_stats
, error_info TSRMLS_CC
);
349 net
->compressed_envelope_packet_no
++;
350 #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
352 size_t decompressed_size
= left
+ MYSQLND_HEADER_SIZE
;
353 zend_uchar
* decompressed_data
= mnd_malloc(decompressed_size
);
354 int error
= net
->m
.decode(decompressed_data
, decompressed_size
,
355 compress_buf
+ MYSQLND_HEADER_SIZE
+ COMPRESSED_HEADER_SIZE
, payload_size
);
358 DBG_INF("success decompressing");
359 for (i
= 0 ; i
< decompressed_size
; i
++) {
360 if (i
&& (i
% 30 == 0)) {
363 printf("%.2X ", (int)*((char*)&(decompressed_data
[i
])));
364 DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data
[i
])));
367 DBG_INF("error decompressing");
369 mnd_free(decompressed_data
);
371 #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
373 #endif /* MYSQLND_COMPRESSION_ENABLED */
375 DBG_INF("no compression");
376 STORE_HEADER_SIZE(safe_storage
, p
);
377 int3store(p
, to_be_sent
);
378 int1store(p
+ 3, net
->packet_no
);
379 bytes_sent
= net
->m
.network_write_ex(net
, p
, to_be_sent
+ MYSQLND_HEADER_SIZE
, conn_stats
, error_info TSRMLS_CC
);
380 RESTORE_HEADER_SIZE(p
, safe_storage
);
381 net
->compressed_envelope_packet_no
++;
389 if left is 0 then there is nothing more to send, but if the last packet was exactly
390 with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
391 empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
392 indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
393 packet will be sent and this loop will end.
395 } while (bytes_sent
&& (left
> 0 || to_be_sent
== MYSQLND_MAX_PACKET_SIZE
));
397 DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC
" packet_no=%u", left
, net
->packet_no
);
399 MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats
,
400 STAT_BYTES_SENT
, count
+ packets_sent
* MYSQLND_HEADER_SIZE
,
401 STAT_PROTOCOL_OVERHEAD_OUT
, packets_sent
* MYSQLND_HEADER_SIZE
,
402 STAT_PACKETS_SENT
, packets_sent
);
405 mnd_efree(compress_buf
);
408 /* Even for zero size payload we have to send a packet */
410 DBG_ERR_FMT("Can't %u send bytes", count
);
411 SET_CLIENT_ERROR(*error_info
, CR_SERVER_GONE_ERROR
, UNKNOWN_SQLSTATE
, mysqlnd_server_gone
);
413 DBG_RETURN(bytes_sent
);
418 #ifdef MYSQLND_COMPRESSION_ENABLED
419 /* {{{ php_mysqlnd_read_buffer_is_empty */
421 php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER
* buffer
)
423 return buffer
->len
? FALSE
:TRUE
;
428 /* {{{ php_mysqlnd_read_buffer_read */
430 php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER
* buffer
, size_t count
, zend_uchar
* dest
)
432 if (buffer
->len
>= count
) {
433 memcpy(dest
, buffer
->data
+ buffer
->offset
, count
);
434 buffer
->offset
+= count
;
435 buffer
->len
-= count
;
441 /* {{{ php_mysqlnd_read_buffer_bytes_left */
443 php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER
* buffer
)
450 /* {{{ php_mysqlnd_read_buffer_free */
452 php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER
** buffer TSRMLS_DC
)
454 DBG_ENTER("php_mysqlnd_read_buffer_free");
456 mnd_efree((*buffer
)->data
);
465 /* {{{ php_mysqlnd_create_read_buffer */
466 static MYSQLND_READ_BUFFER
*
467 mysqlnd_create_read_buffer(size_t count TSRMLS_DC
)
469 MYSQLND_READ_BUFFER
* ret
= mnd_emalloc(sizeof(MYSQLND_READ_BUFFER
));
470 DBG_ENTER("mysqlnd_create_read_buffer");
471 ret
->is_empty
= php_mysqlnd_read_buffer_is_empty
;
472 ret
->read
= php_mysqlnd_read_buffer_read
;
473 ret
->bytes_left
= php_mysqlnd_read_buffer_bytes_left
;
474 ret
->free_buffer
= php_mysqlnd_read_buffer_free
;
475 ret
->data
= mnd_emalloc(count
);
476 ret
->size
= ret
->len
= count
;
483 /* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */
484 static enum_func_status
485 MYSQLND_METHOD(mysqlnd_net
, read_compressed_packet_from_stream_and_fill_read_buffer
)
486 (MYSQLND_NET
* net
, size_t net_payload_size
, MYSQLND_STATS
* conn_stats
, MYSQLND_ERROR_INFO
* error_info TSRMLS_DC
)
488 size_t decompressed_size
;
489 enum_func_status ret
= PASS
;
490 zend_uchar
* compressed_data
= NULL
;
491 zend_uchar comp_header
[COMPRESSED_HEADER_SIZE
];
492 DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffe");
494 /* Read the compressed header */
495 if (FAIL
== net
->m
.network_read_ex(net
, comp_header
, COMPRESSED_HEADER_SIZE
, conn_stats
, error_info TSRMLS_CC
)) {
498 decompressed_size
= uint3korr(comp_header
);
500 /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
501 /* we need to decompress the data */
503 if (decompressed_size
) {
504 compressed_data
= mnd_emalloc(net_payload_size
);
505 if (FAIL
== net
->m
.network_read_ex(net
, compressed_data
, net_payload_size
, conn_stats
, error_info TSRMLS_CC
)) {
509 net
->uncompressed_data
= mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC
);
510 ret
= net
->m
.decode(net
->uncompressed_data
->data
, decompressed_size
, compressed_data
, net_payload_size TSRMLS_CC
);
515 DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size
);
516 net
->uncompressed_data
= mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC
);
517 if (FAIL
== net
->m
.network_read_ex(net
, net
->uncompressed_data
->data
, net_payload_size
, conn_stats
, error_info TSRMLS_CC
)) {
523 if (compressed_data
) {
524 mnd_efree(compressed_data
);
529 #endif /* MYSQLND_COMPRESSION_ENABLED */
532 /* {{{ mysqlnd_net::decode */
533 static enum_func_status
534 MYSQLND_METHOD(mysqlnd_net
, decode
)(zend_uchar
* uncompressed_data
, const size_t uncompressed_data_len
,
535 const zend_uchar
* const compressed_data
, const size_t compressed_data_len TSRMLS_DC
)
537 #ifdef MYSQLND_COMPRESSION_ENABLED
539 uLongf tmp_complen
= uncompressed_data_len
;
540 DBG_ENTER("mysqlnd_net::decode");
541 error
= uncompress(uncompressed_data
, &tmp_complen
, compressed_data
, compressed_data_len
);
543 DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC
, tmp_complen
, compressed_data_len
);
545 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
);
547 DBG_RETURN(error
== Z_OK
? PASS
:FAIL
);
549 DBG_ENTER("mysqlnd_net::decode");
556 /* {{{ mysqlnd_net::encode */
557 static enum_func_status
558 MYSQLND_METHOD(mysqlnd_net
, encode
)(zend_uchar
* compress_buffer
, size_t * compress_buffer_len
,
559 const zend_uchar
* const uncompressed_data
, const size_t uncompressed_data_len TSRMLS_DC
)
561 #ifdef MYSQLND_COMPRESSION_ENABLED
563 uLongf tmp_complen
= *compress_buffer_len
;
564 DBG_ENTER("mysqlnd_net::encode");
565 error
= compress(compress_buffer
, &tmp_complen
, uncompressed_data
, uncompressed_data_len
);
568 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
);
570 *compress_buffer_len
= tmp_complen
;
571 DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen
);
574 DBG_RETURN(error
== Z_OK
? PASS
:FAIL
);
576 DBG_ENTER("mysqlnd_net::encode");
583 /* {{{ mysqlnd_net::receive_ex */
584 static enum_func_status
585 MYSQLND_METHOD(mysqlnd_net
, receive_ex
)(MYSQLND_NET
* const net
, zend_uchar
* const buffer
, const size_t count
,
586 MYSQLND_STATS
* const conn_stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
588 size_t to_read
= count
;
589 zend_uchar
* p
= buffer
;
591 DBG_ENTER("mysqlnd_net::receive_ex");
592 #ifdef MYSQLND_COMPRESSION_ENABLED
593 if (net
->compressed
) {
594 if (net
->uncompressed_data
) {
595 size_t to_read_from_buffer
= MIN(net
->uncompressed_data
->bytes_left(net
->uncompressed_data
), to_read
);
596 DBG_INF_FMT("reading %u from uncompressed_data buffer", to_read_from_buffer
);
597 if (to_read_from_buffer
) {
598 net
->uncompressed_data
->read(net
->uncompressed_data
, to_read_from_buffer
, (zend_uchar
*) p
);
599 p
+= to_read_from_buffer
;
600 to_read
-= to_read_from_buffer
;
602 DBG_INF_FMT("left %u to read", to_read
);
603 if (TRUE
== net
->uncompressed_data
->is_empty(net
->uncompressed_data
)) {
604 /* Everything was consumed. This should never happen here, but for security */
605 net
->uncompressed_data
->free_buffer(&net
->uncompressed_data TSRMLS_CC
);
609 zend_uchar net_header
[MYSQLND_HEADER_SIZE
];
610 size_t net_payload_size
;
611 zend_uchar packet_no
;
613 if (FAIL
== net
->m
.network_read_ex(net
, net_header
, MYSQLND_HEADER_SIZE
, conn_stats
, error_info TSRMLS_CC
)) {
616 net_payload_size
= uint3korr(net_header
);
617 packet_no
= uint1korr(net_header
+ 3);
618 if (net
->compressed_envelope_packet_no
!= packet_no
) {
619 DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC
,
620 net
->compressed_envelope_packet_no
, packet_no
, net_payload_size
);
622 php_error(E_WARNING
, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC
,
623 net
->compressed_envelope_packet_no
, packet_no
, net_payload_size
);
626 net
->compressed_envelope_packet_no
++;
627 #ifdef MYSQLND_DUMP_HEADER_N_BODY
628 DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no
, (unsigned long) net_payload_size
);
630 /* Now let's read from the wire, decompress it and fill the read buffer */
631 net
->m
.read_compressed_packet_from_stream_and_fill_read_buffer(net
, net_payload_size
, conn_stats
, error_info TSRMLS_CC
);
634 Now a bit of recursion - read from the read buffer,
635 if the data which we have just read from the wire
636 is not enough, then the recursive call will try to
637 satisfy it until it is satisfied.
639 DBG_RETURN(net
->m
.receive_ex(net
, p
, to_read
, conn_stats
, error_info TSRMLS_CC
));
643 #endif /* MYSQLND_COMPRESSION_ENABLED */
644 DBG_RETURN(net
->m
.network_read_ex(net
, p
, to_read
, conn_stats
, error_info TSRMLS_CC
));
649 /* {{{ mysqlnd_net::set_client_option */
650 static enum_func_status
651 MYSQLND_METHOD(mysqlnd_net
, set_client_option
)(MYSQLND_NET
* const net
, enum mysqlnd_option option
, const char * const value TSRMLS_DC
)
653 DBG_ENTER("mysqlnd_net::set_client_option");
654 DBG_INF_FMT("option=%u", option
);
656 case MYSQLND_OPT_NET_CMD_BUFFER_SIZE
:
657 DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
658 if (*(unsigned int*) value
< MYSQLND_NET_CMD_BUFFER_MIN_SIZE
) {
661 net
->cmd_buffer
.length
= *(unsigned int*) value
;
662 DBG_INF_FMT("new_length=%u", net
->cmd_buffer
.length
);
663 if (!net
->cmd_buffer
.buffer
) {
664 net
->cmd_buffer
.buffer
= mnd_pemalloc(net
->cmd_buffer
.length
, net
->persistent
);
666 net
->cmd_buffer
.buffer
= mnd_perealloc(net
->cmd_buffer
.buffer
, net
->cmd_buffer
.length
, net
->persistent
);
669 case MYSQLND_OPT_NET_READ_BUFFER_SIZE
:
670 DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
671 net
->options
.net_read_buffer_size
= *(unsigned int*) value
;
672 DBG_INF_FMT("new_length=%u", net
->options
.net_read_buffer_size
);
674 case MYSQL_OPT_CONNECT_TIMEOUT
:
675 DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
676 net
->options
.timeout_connect
= *(unsigned int*) value
;
678 case MYSQLND_OPT_SSL_KEY
:
680 zend_bool pers
= net
->persistent
;
681 if (net
->options
.ssl_key
) {
682 mnd_pefree(net
->options
.ssl_key
, pers
);
684 net
->options
.ssl_key
= value
? mnd_pestrdup(value
, pers
) : NULL
;
687 case MYSQLND_OPT_SSL_CERT
:
689 zend_bool pers
= net
->persistent
;
690 if (net
->options
.ssl_cert
) {
691 mnd_pefree(net
->options
.ssl_cert
, pers
);
693 net
->options
.ssl_cert
= value
? mnd_pestrdup(value
, pers
) : NULL
;
696 case MYSQLND_OPT_SSL_CA
:
698 zend_bool pers
= net
->persistent
;
699 if (net
->options
.ssl_ca
) {
700 mnd_pefree(net
->options
.ssl_ca
, pers
);
702 net
->options
.ssl_ca
= value
? mnd_pestrdup(value
, pers
) : NULL
;
705 case MYSQLND_OPT_SSL_CAPATH
:
707 zend_bool pers
= net
->persistent
;
708 if (net
->options
.ssl_capath
) {
709 mnd_pefree(net
->options
.ssl_capath
, pers
);
711 net
->options
.ssl_capath
= value
? mnd_pestrdup(value
, pers
) : NULL
;
714 case MYSQLND_OPT_SSL_CIPHER
:
716 zend_bool pers
= net
->persistent
;
717 if (net
->options
.ssl_cipher
) {
718 mnd_pefree(net
->options
.ssl_cipher
, pers
);
720 net
->options
.ssl_cipher
= value
? mnd_pestrdup(value
, pers
) : NULL
;
723 case MYSQLND_OPT_SSL_PASSPHRASE
:
725 zend_bool pers
= net
->persistent
;
726 if (net
->options
.ssl_passphrase
) {
727 mnd_pefree(net
->options
.ssl_passphrase
, pers
);
729 net
->options
.ssl_passphrase
= value
? mnd_pestrdup(value
, pers
) : NULL
;
732 case MYSQL_OPT_SSL_VERIFY_SERVER_CERT
:
733 net
->options
.ssl_verify_peer
= value
? ((*(zend_bool
*)value
)? TRUE
:FALSE
): FALSE
;
735 case MYSQL_OPT_READ_TIMEOUT
:
736 net
->options
.timeout_read
= *(unsigned int*) value
;
738 #ifdef WHEN_SUPPORTED_BY_MYSQLI
739 case MYSQL_OPT_WRITE_TIMEOUT
:
740 net
->options
.timeout_write
= *(unsigned int*) value
;
743 case MYSQL_OPT_COMPRESS
:
744 net
->options
.flags
|= MYSQLND_NET_FLAG_USE_COMPRESSION
;
753 /* {{{ mysqlnd_net::consume_uneaten_data */
755 MYSQLND_METHOD(mysqlnd_net
, consume_uneaten_data
)(MYSQLND_NET
* const net
, enum php_mysqlnd_server_command cmd TSRMLS_DC
)
757 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
759 Switch to non-blocking mode and try to consume something from
760 the line, if possible, then continue. This saves us from looking for
761 the actuall place where out-of-order packets have been sent.
762 If someone is completely sure that everything is fine, he can switch it
766 size_t skipped_bytes
= 0;
767 int opt
= PHP_STREAM_OPTION_BLOCKING
;
768 int was_blocked
= net
->stream
->ops
->set_option(net
->stream
, opt
, 0, NULL TSRMLS_CC
);
770 DBG_ENTER("mysqlnd_net::consume_uneaten_data");
772 if (PHP_STREAM_OPTION_RETURN_ERR
!= was_blocked
) {
773 /* Do a read of 1 byte */
777 skipped_bytes
+= (bytes_consumed
= php_stream_read(net
->stream
, tmp_buf
, sizeof(tmp_buf
)));
778 } while (bytes_consumed
== sizeof(tmp_buf
));
781 net
->stream
->ops
->set_option(net
->stream
, opt
, 1, NULL TSRMLS_CC
);
784 if (bytes_consumed
) {
785 DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
786 bytes_consumed
, mysqlnd_command_to_text
[net
->last_command
]);
787 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Skipped %u bytes. Last command %s hasn't "
788 "consumed all the output from the server",
789 bytes_consumed
, mysqlnd_command_to_text
[net
->last_command
]);
792 net
->last_command
= cmd
;
794 DBG_RETURN(skipped_bytes
);
802 in libmyusql, if cert and !key then key=cert
804 /* {{{ mysqlnd_net::enable_ssl */
805 static enum_func_status
806 MYSQLND_METHOD(mysqlnd_net
, enable_ssl
)(MYSQLND_NET
* const net TSRMLS_DC
)
808 #ifdef MYSQLND_SSL_SUPPORTED
809 php_stream_context
*context
= php_stream_context_alloc(TSRMLS_C
);
810 DBG_ENTER("mysqlnd_net::enable_ssl");
815 if (net
->options
.ssl_key
) {
817 ZVAL_STRING(&key_zval
, net
->options
.ssl_key
, 0);
818 php_stream_context_set_option(context
, "ssl", "local_pk", &key_zval
);
820 if (net
->options
.ssl_verify_peer
) {
821 zval verify_peer_zval
;
822 ZVAL_TRUE(&verify_peer_zval
);
823 php_stream_context_set_option(context
, "ssl", "verify_peer", &verify_peer_zval
);
825 if (net
->options
.ssl_cert
) {
827 ZVAL_STRING(&cert_zval
, net
->options
.ssl_cert
, 0);
828 php_stream_context_set_option(context
, "ssl", "local_cert", &cert_zval
);
829 if (!net
->options
.ssl_key
) {
830 php_stream_context_set_option(context
, "ssl", "local_pk", &cert_zval
);
833 if (net
->options
.ssl_ca
) {
835 ZVAL_STRING(&cafile_zval
, net
->options
.ssl_ca
, 0);
836 php_stream_context_set_option(context
, "ssl", "cafile", &cafile_zval
);
838 if (net
->options
.ssl_capath
) {
840 ZVAL_STRING(&capath_zval
, net
->options
.ssl_capath
, 0);
841 php_stream_context_set_option(context
, "ssl", "cafile", &capath_zval
);
843 if (net
->options
.ssl_passphrase
) {
844 zval passphrase_zval
;
845 ZVAL_STRING(&passphrase_zval
, net
->options
.ssl_passphrase
, 0);
846 php_stream_context_set_option(context
, "ssl", "passphrase", &passphrase_zval
);
848 if (net
->options
.ssl_cipher
) {
850 ZVAL_STRING(&cipher_zval
, net
->options
.ssl_cipher
, 0);
851 php_stream_context_set_option(context
, "ssl", "ciphers", &cipher_zval
);
853 php_stream_context_set(net
->stream
, context
);
854 if (php_stream_xport_crypto_setup(net
->stream
, STREAM_CRYPTO_METHOD_TLS_CLIENT
, NULL TSRMLS_CC
) < 0 ||
855 php_stream_xport_crypto_enable(net
->stream
, 1 TSRMLS_CC
) < 0)
857 DBG_ERR("Cannot connect to MySQL by using SSL");
858 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Cannot connect to MySQL by using SSL");
862 get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
863 then the context would not survive cleaning of EG(regular_list), where it is registered, as a
864 resource. What happens is that after this destruction any use of the network will mean usage
865 of the context, which means usage of already freed memory, bad. Actually we don't need this
866 context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
868 php_stream_context_set(net
->stream
, NULL
);
870 if (net
->options
.timeout_read
) {
872 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net
->options
.timeout_read
);
873 tv
.tv_sec
= net
->options
.timeout_read
;
875 php_stream_set_option(net
->stream
, PHP_STREAM_OPTION_READ_TIMEOUT
, 0, &tv
);
880 DBG_ENTER("mysqlnd_net::enable_ssl");
887 /* {{{ mysqlnd_net::disable_ssl */
888 static enum_func_status
889 MYSQLND_METHOD(mysqlnd_net
, disable_ssl
)(MYSQLND_NET
* const net TSRMLS_DC
)
891 DBG_ENTER("mysqlnd_net::disable_ssl");
897 /* {{{ mysqlnd_net::free_contents */
899 MYSQLND_METHOD(mysqlnd_net
, free_contents
)(MYSQLND_NET
* net TSRMLS_DC
)
901 zend_bool pers
= net
->persistent
;
902 DBG_ENTER("mysqlnd_net::free_contents");
904 #ifdef MYSQLND_COMPRESSION_ENABLED
905 if (net
->uncompressed_data
) {
906 net
->uncompressed_data
->free_buffer(&net
->uncompressed_data TSRMLS_CC
);
909 if (net
->options
.ssl_key
) {
910 mnd_pefree(net
->options
.ssl_key
, pers
);
911 net
->options
.ssl_key
= NULL
;
913 if (net
->options
.ssl_cert
) {
914 mnd_pefree(net
->options
.ssl_cert
, pers
);
915 net
->options
.ssl_cert
= NULL
;
917 if (net
->options
.ssl_ca
) {
918 mnd_pefree(net
->options
.ssl_ca
, pers
);
919 net
->options
.ssl_ca
= NULL
;
921 if (net
->options
.ssl_capath
) {
922 mnd_pefree(net
->options
.ssl_capath
, pers
);
923 net
->options
.ssl_capath
= NULL
;
925 if (net
->options
.ssl_cipher
) {
926 mnd_pefree(net
->options
.ssl_cipher
, pers
);
927 net
->options
.ssl_cipher
= NULL
;
935 /* {{{ mysqlnd_net::close_stream */
937 MYSQLND_METHOD(mysqlnd_net
, close_stream
)(MYSQLND_NET
* const net
, MYSQLND_STATS
* const stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
939 DBG_ENTER("mysqlnd_net::close_stream");
940 if (net
&& net
->stream
) {
941 zend_bool pers
= net
->persistent
;
942 DBG_INF_FMT("Freeing stream. abstract=%p", net
->stream
->abstract
);
945 php_stream_free(net
->stream
, PHP_STREAM_FREE_CLOSE_PERSISTENT
| PHP_STREAM_FREE_RSRC_DTOR
);
948 otherwise we will crash because the EG(persistent_list) has been freed already,
949 before the modules are shut down
951 php_stream_free(net
->stream
, PHP_STREAM_FREE_CLOSE
| PHP_STREAM_FREE_RSRC_DTOR
);
954 php_stream_free(net
->stream
, PHP_STREAM_FREE_CLOSE
);
964 /* {{{ mysqlnd_net::init */
965 static enum_func_status
966 MYSQLND_METHOD(mysqlnd_net
, init
)(MYSQLND_NET
* const net
, MYSQLND_STATS
* const stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
968 unsigned int buf_size
;
969 DBG_ENTER("mysqlnd_net::init");
971 buf_size
= MYSQLND_G(net_cmd_buffer_size
); /* this is long, cast to unsigned int*/
972 net
->m
.set_client_option(net
, MYSQLND_OPT_NET_CMD_BUFFER_SIZE
, (char *) &buf_size TSRMLS_CC
);
974 buf_size
= MYSQLND_G(net_read_buffer_size
); /* this is long, cast to unsigned int*/
975 net
->m
.set_client_option(net
, MYSQLND_OPT_NET_READ_BUFFER_SIZE
, (char *)&buf_size TSRMLS_CC
);
977 buf_size
= MYSQLND_G(net_read_timeout
); /* this is long, cast to unsigned int*/
978 net
->m
.set_client_option(net
, MYSQL_OPT_READ_TIMEOUT
, (char *)&buf_size TSRMLS_CC
);
985 /* {{{ mysqlnd_net::dtor */
987 MYSQLND_METHOD(mysqlnd_net
, dtor
)(MYSQLND_NET
* const net
, MYSQLND_STATS
* const stats
, MYSQLND_ERROR_INFO
* const error_info TSRMLS_DC
)
989 DBG_ENTER("mysqlnd_net::dtor");
991 zend_bool pers
= net
->persistent
;
993 net
->m
.free_contents(net TSRMLS_CC
);
994 net
->m
.close_stream(net
, stats
, error_info TSRMLS_CC
);
995 if (net
->cmd_buffer
.buffer
) {
996 DBG_INF("Freeing cmd buffer");
997 mnd_pefree(net
->cmd_buffer
.buffer
, pers
);
998 net
->cmd_buffer
.buffer
= NULL
;
1000 mnd_pefree(net
, pers
);
1007 MYSQLND_CLASS_METHODS_START(mysqlnd_net
)
1008 MYSQLND_METHOD(mysqlnd_net
, init
),
1009 MYSQLND_METHOD(mysqlnd_net
, dtor
),
1010 MYSQLND_METHOD(mysqlnd_net
, connect_ex
),
1011 MYSQLND_METHOD(mysqlnd_net
, close_stream
),
1012 MYSQLND_METHOD(mysqlnd_net
, open_pipe
),
1013 MYSQLND_METHOD(mysqlnd_net
, open_tcp_or_unix
),
1014 NULL
, /* unused 1 */
1015 NULL
, /* unused 2 */
1016 MYSQLND_METHOD(mysqlnd_net
, post_connect_set_opt
),
1017 MYSQLND_METHOD(mysqlnd_net
, set_client_option
),
1018 MYSQLND_METHOD(mysqlnd_net
, decode
),
1019 MYSQLND_METHOD(mysqlnd_net
, encode
),
1020 MYSQLND_METHOD(mysqlnd_net
, consume_uneaten_data
),
1021 MYSQLND_METHOD(mysqlnd_net
, free_contents
),
1022 MYSQLND_METHOD(mysqlnd_net
, enable_ssl
),
1023 MYSQLND_METHOD(mysqlnd_net
, disable_ssl
),
1024 MYSQLND_METHOD(mysqlnd_net
, network_read_ex
),
1025 MYSQLND_METHOD(mysqlnd_net
, network_write_ex
),
1026 MYSQLND_METHOD(mysqlnd_net
, send_ex
),
1027 MYSQLND_METHOD(mysqlnd_net
, receive_ex
),
1028 #ifdef MYSQLND_COMPRESSION_ENABLED
1029 MYSQLND_METHOD(mysqlnd_net
, read_compressed_packet_from_stream_and_fill_read_buffer
)
1033 MYSQLND_CLASS_METHODS_END
;
1036 /* {{{ mysqlnd_net_init */
1037 PHPAPI MYSQLND_NET
*
1038 mysqlnd_net_init(zend_bool persistent
, MYSQLND_STATS
* stats
, MYSQLND_ERROR_INFO
* error_info TSRMLS_DC
)
1041 DBG_ENTER("mysqlnd_net_init");
1042 net
= MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory
).get_io_channel(persistent
, stats
, error_info TSRMLS_CC
);
1048 /* {{{ mysqlnd_net_free */
1050 mysqlnd_net_free(MYSQLND_NET
* const net
, MYSQLND_STATS
* stats
, MYSQLND_ERROR_INFO
* error_info TSRMLS_DC
)
1052 DBG_ENTER("mysqlnd_net_free");
1054 net
->m
.dtor(net
, stats
, error_info TSRMLS_CC
);
1067 * vim600: noet sw=4 ts=4 fdm=marker
1068 * vim<600: noet sw=4 ts=4