Busybox: Upgrade to 1.21.1 (stable). lsof active.
[tomato.git] / release / src / router / php / ext / mysqlnd / mysqlnd_net.c
blob21241b3ef506593ea591d8195b2ff4e924a0c329
1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
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 $ */
22 #include "php.h"
23 #include "php_globals.h"
24 #include "mysqlnd.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"
31 #include "zend_ini.h"
32 #ifdef MYSQLND_COMPRESSION_ENABLED
33 #include <zlib.h>
34 #endif
36 #ifndef PHP_WIN32
37 #include <netinet/tcp.h>
38 #else
39 #include <winsock.h>
40 #endif
43 /* {{{ mysqlnd_set_sock_no_delay */
44 static int
45 mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_DC)
48 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
49 int ret = SUCCESS;
50 int flag = 1;
51 int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
53 DBG_ENTER("mysqlnd_set_sock_no_delay");
55 if (result == -1) {
56 ret = FAILURE;
59 DBG_RETURN(ret);
61 /* }}} */
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);
78 while (to_read) {
79 if (!(ret = php_stream_read(net->stream, (char *) p, to_read))) {
80 DBG_ERR_FMT("Error while reading header from socket");
81 return_value = FAIL;
82 break;
84 p += ret;
85 to_read -= ret;
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);
91 /* }}} */
94 /* {{{ mysqlnd_net::network_write_ex */
95 static size_t
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)
99 size_t ret;
100 DBG_ENTER("mysqlnd_net::network_write_ex");
101 ret = php_stream_write(net->stream, (char *)buffer, count);
102 DBG_RETURN(ret);
104 /* }}} */
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;
114 #else
115 unsigned int streams_options = 0;
116 #endif
117 DBG_ENTER("mysqlnd_net::open_pipe");
118 if (persistent) {
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);
123 if (!net->stream) {
124 SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
125 DBG_RETURN(FAIL);
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;
136 DBG_RETURN(PASS);
138 /* }}} */
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;
149 #else
150 unsigned int streams_options = 0;
151 #endif
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;
156 int errcode = 0;
157 struct timeval tv;
159 DBG_ENTER("mysqlnd_net::open_tcp_or_unix");
161 if (persistent) {
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;
168 tv.tv_usec = 0;
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) {
176 DBG_ERR("Error");
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);
182 if (errstr) {
183 /* no mnd_ since we don't allocate it */
184 efree(errstr);
186 DBG_RETURN(FAIL);
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;
206 #if ZEND_DEBUG
207 /* Shut-up the streams, they don't know what they are doing */
208 net->stream->__exposed = 1;
209 #endif
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;
222 DBG_RETURN(PASS);
224 /* }}} */
227 /* {{{ mysqlnd_net::connect_ex */
228 static void
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) {
235 struct timeval tv;
236 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read);
237 tv.tv_sec = net->options.timeout_read;
238 tv.tv_usec = 0;
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);
247 DBG_VOID_RETURN;
249 /* }}} */
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);
273 DBG_RETURN(ret);
275 /* }}} */
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)
299 static size_t
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;
306 size_t left = count;
307 zend_uchar * p = (zend_uchar *) buffer;
308 zend_uchar * compress_buf = NULL;
309 size_t to_be_sent;
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);
320 do {
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;
326 size_t payload_size;
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;
337 } else {
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
351 if (res == Z_OK) {
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);
356 if (error == Z_OK) {
357 int i;
358 DBG_INF("success decompressing");
359 for (i = 0 ; i < decompressed_size; i++) {
360 if (i && (i % 30 == 0)) {
361 printf("\n\t\t");
363 printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
364 DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
366 } else {
367 DBG_INF("error decompressing");
369 mnd_free(decompressed_data);
371 #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
372 } else
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++;
383 net->packet_no++;
385 p += to_be_sent;
386 left -= to_be_sent;
387 packets_sent++;
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);
404 if (compress_buf) {
405 mnd_efree(compress_buf);
408 /* Even for zero size payload we have to send a packet */
409 if (!bytes_sent) {
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);
415 /* }}} */
418 #ifdef MYSQLND_COMPRESSION_ENABLED
419 /* {{{ php_mysqlnd_read_buffer_is_empty */
420 static zend_bool
421 php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
423 return buffer->len? FALSE:TRUE;
425 /* }}} */
428 /* {{{ php_mysqlnd_read_buffer_read */
429 static void
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;
438 /* }}} */
441 /* {{{ php_mysqlnd_read_buffer_bytes_left */
442 static size_t
443 php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
445 return buffer->len;
447 /* }}} */
450 /* {{{ php_mysqlnd_read_buffer_free */
451 static void
452 php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
454 DBG_ENTER("php_mysqlnd_read_buffer_free");
455 if (*buffer) {
456 mnd_efree((*buffer)->data);
457 mnd_efree(*buffer);
458 *buffer = NULL;
460 DBG_VOID_RETURN;
462 /* }}} */
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;
477 ret->offset = 0;
478 DBG_RETURN(ret);
480 /* }}} */
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)) {
496 DBG_RETURN(FAIL);
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)) {
506 ret = FAIL;
507 goto end;
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);
511 if (ret == FAIL) {
512 goto end;
514 } else {
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)) {
518 ret = FAIL;
519 goto end;
522 end:
523 if (compressed_data) {
524 mnd_efree(compressed_data);
526 DBG_RETURN(ret);
528 /* }}} */
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
538 int error;
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);
544 if (error != Z_OK) {
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);
548 #else
549 DBG_ENTER("mysqlnd_net::decode");
550 DBG_RETURN(FAIL);
551 #endif
553 /* }}} */
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
562 int error;
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);
567 if (error != Z_OK) {
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);
569 } else {
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);
575 #else
576 DBG_ENTER("mysqlnd_net::encode");
577 DBG_RETURN(FAIL);
578 #endif
580 /* }}} */
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);
608 if (to_read) {
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)) {
614 DBG_RETURN(FAIL);
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);
624 DBG_RETURN(FAIL);
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);
629 #endif
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));
641 DBG_RETURN(PASS);
643 #endif /* MYSQLND_COMPRESSION_ENABLED */
644 DBG_RETURN(net->m.network_read_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC));
646 /* }}} */
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);
655 switch (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) {
659 DBG_RETURN(FAIL);
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);
665 } else {
666 net->cmd_buffer.buffer = mnd_perealloc(net->cmd_buffer.buffer, net->cmd_buffer.length, net->persistent);
668 break;
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);
673 break;
674 case MYSQL_OPT_CONNECT_TIMEOUT:
675 DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
676 net->options.timeout_connect = *(unsigned int*) value;
677 break;
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;
685 break;
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;
694 break;
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;
703 break;
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;
712 break;
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;
721 break;
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;
730 break;
732 case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
733 net->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE;
734 break;
735 case MYSQL_OPT_READ_TIMEOUT:
736 net->options.timeout_read = *(unsigned int*) value;
737 break;
738 #ifdef WHEN_SUPPORTED_BY_MYSQLI
739 case MYSQL_OPT_WRITE_TIMEOUT:
740 net->options.timeout_write = *(unsigned int*) value;
741 break;
742 #endif
743 case MYSQL_OPT_COMPRESS:
744 net->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION;
745 break;
746 default:
747 DBG_RETURN(FAIL);
749 DBG_RETURN(PASS);
751 /* }}} */
753 /* {{{ mysqlnd_net::consume_uneaten_data */
754 size_t
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
763 off.
765 char tmp_buf[256];
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 */
774 int bytes_consumed;
776 do {
777 skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf)));
778 } while (bytes_consumed == sizeof(tmp_buf));
780 if (was_blocked) {
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);
795 #else
796 return 0;
797 #endif
799 /* }}} */
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");
811 if (!context) {
812 DBG_RETURN(FAIL);
815 if (net->options.ssl_key) {
816 zval key_zval;
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) {
826 zval cert_zval;
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) {
834 zval cafile_zval;
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) {
839 zval capath_zval;
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) {
849 zval cipher_zval;
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");
859 DBG_RETURN(FAIL);
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) {
871 struct timeval tv;
872 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read);
873 tv.tv_sec = net->options.timeout_read;
874 tv.tv_usec = 0;
875 php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
878 DBG_RETURN(PASS);
879 #else
880 DBG_ENTER("mysqlnd_net::enable_ssl");
881 DBG_RETURN(PASS);
882 #endif
884 /* }}} */
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");
892 DBG_RETURN(PASS);
894 /* }}} */
897 /* {{{ mysqlnd_net::free_contents */
898 static void
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);
908 #endif
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;
930 DBG_VOID_RETURN;
932 /* }}} */
935 /* {{{ mysqlnd_net::close_stream */
936 static void
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);
943 if (pers) {
944 if (EG(active)) {
945 php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
946 } else {
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);
953 } else {
954 php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE);
956 net->stream = NULL;
959 DBG_VOID_RETURN;
961 /* }}} */
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);
980 DBG_RETURN(PASS);
982 /* }}} */
985 /* {{{ mysqlnd_net::dtor */
986 static void
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");
990 if (net) {
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);
1002 DBG_VOID_RETURN;
1004 /* }}} */
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)
1030 #else
1031 NULL
1032 #endif
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)
1040 MYSQLND_NET * net;
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);
1043 DBG_RETURN(net);
1045 /* }}} */
1048 /* {{{ mysqlnd_net_free */
1049 PHPAPI void
1050 mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
1052 DBG_ENTER("mysqlnd_net_free");
1053 if (net) {
1054 net->m.dtor(net, stats, error_info TSRMLS_CC);
1056 DBG_VOID_RETURN;
1058 /* }}} */
1063 * Local variables:
1064 * tab-width: 4
1065 * c-basic-offset: 4
1066 * End:
1067 * vim600: noet sw=4 ts=4 fdm=marker
1068 * vim<600: noet sw=4 ts=4