Back to index

php5  5.3.10
mysqlnd_net.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2006-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Georg Richter <georg@mysql.com>                             |
00016   |          Andrey Hristov <andrey@mysql.com>                           |
00017   |          Ulf Wendel <uwendel@mysql.com>                              |
00018   +----------------------------------------------------------------------+
00019 */
00020 #include "php.h"
00021 #include "php_globals.h"
00022 #include "mysqlnd.h"
00023 #include "mysqlnd_priv.h"
00024 #include "mysqlnd_wireprotocol.h"
00025 #include "mysqlnd_statistics.h"
00026 #include "mysqlnd_debug.h"
00027 #include "ext/standard/sha1.h"
00028 #include "php_network.h"
00029 #include "zend_ini.h"
00030 #ifdef MYSQLND_COMPRESSION_ENABLED
00031 #include <zlib.h>
00032 #endif
00033 
00034 #ifndef PHP_WIN32
00035 #include <netinet/tcp.h>
00036 #else
00037 #include <winsock.h>
00038 #endif
00039 
00040 
00041 /* {{{ mysqlnd_set_sock_no_delay */
00042 static int
00043 mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_DC)
00044 {
00045 
00046        int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
00047        int ret = SUCCESS;
00048        int flag = 1;
00049        int result = setsockopt(socketd, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int));
00050 
00051        DBG_ENTER("mysqlnd_set_sock_no_delay");
00052 
00053        if (result == -1) {
00054               ret = FAILURE;
00055        }
00056 
00057        DBG_RETURN(ret);
00058 }
00059 /* }}} */
00060 
00061 
00062 /* {{{ mysqlnd_net::network_read */
00063 static enum_func_status
00064 MYSQLND_METHOD(mysqlnd_net, network_read)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
00065 {
00066        enum_func_status return_value = PASS;
00067        size_t to_read = count, ret;
00068        size_t old_chunk_size = conn->net->stream->chunk_size;
00069        DBG_ENTER("mysqlnd_net::network_read");
00070        DBG_INF_FMT("count=%u", count);
00071        conn->net->stream->chunk_size = MIN(to_read, conn->net->options.net_read_buffer_size);
00072        while (to_read) {
00073               if (!(ret = php_stream_read(conn->net->stream, (char *) buffer, to_read))) {
00074                      DBG_ERR_FMT("Error while reading header from socket");
00075                      return_value = FAIL;
00076                      break;
00077               }
00078               buffer += ret;
00079               to_read -= ret;
00080        }
00081        MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_BYTES_RECEIVED, count - to_read);
00082        conn->net->stream->chunk_size = old_chunk_size;
00083        DBG_RETURN(return_value);
00084 }
00085 /* }}} */
00086 
00087 
00088 /* {{{ mysqlnd_net::network_write */
00089 static size_t
00090 MYSQLND_METHOD(mysqlnd_net, network_write)(MYSQLND * const conn, const zend_uchar * const buf, size_t count TSRMLS_DC)
00091 {
00092        size_t ret;
00093        DBG_ENTER("mysqlnd_net::network_write");
00094        ret = php_stream_write(conn->net->stream, (char *)buf, count);
00095        DBG_RETURN(ret);
00096 }
00097 /* }}} */
00098 
00099 
00100 
00101 /* {{{ mysqlnd_net::connect */
00102 static enum_func_status
00103 MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * net, const char * const scheme, size_t scheme_len, zend_bool persistent, char **errstr, int * errcode TSRMLS_DC)
00104 {
00105 #if PHP_API_VERSION < 20100412
00106        unsigned int streams_options = ENFORCE_SAFE_MODE;
00107 #else
00108        unsigned int streams_options = 0;
00109 #endif
00110        unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
00111        char * hashed_details = NULL;
00112        int hashed_details_len = 0;
00113        struct timeval tv;
00114        DBG_ENTER("mysqlnd_net::connect");
00115 
00116        if (persistent) {
00117               hashed_details_len = spprintf(&hashed_details, 0, "%p", net);
00118               DBG_INF_FMT("hashed_details=%s", hashed_details);
00119        }
00120 
00121        net->packet_no = net->compressed_envelope_packet_no = 0;
00122 
00123        if (net->stream) {
00124               /* close before opening a new one */
00125               DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract);
00126               if (net->persistent) {
00127                      php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
00128               } else {
00129                      php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE);
00130               }
00131               net->stream = NULL;
00132        }
00133 
00134        if (net->options.timeout_connect) {
00135               tv.tv_sec = net->options.timeout_connect;
00136               tv.tv_usec = 0;
00137        }
00138 
00139        DBG_INF_FMT("calling php_stream_xport_create");
00140        net->stream = php_stream_xport_create(scheme, scheme_len, streams_options, streams_flags,
00141                                                                         hashed_details, (net->options.timeout_connect) ? &tv : NULL,
00142                                                                         NULL /*ctx*/, errstr, errcode);
00143 
00144        if (*errstr || !net->stream) {
00145               if (hashed_details) {
00146                      efree(hashed_details); /* allocated by spprintf */
00147               }
00148               *errcode = CR_CONNECTION_ERROR;
00149               DBG_RETURN(FAIL);
00150        }
00151 
00152        if (hashed_details) {
00153               /*
00154                 If persistent, the streams register it in EG(persistent_list).
00155                 This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
00156                 whatever they have to.
00157               */
00158               zend_rsrc_list_entry *le;
00159 
00160               if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, (void*) &le) == SUCCESS) {
00161                      /*
00162                        in_free will let streams code skip destructing - big HACK,
00163                        but STREAMS suck big time regarding persistent streams.
00164                        Just not compatible for extensions that need persistency.
00165                      */
00166                      net->stream->in_free = 1;
00167                      zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_len + 1);
00168                      net->stream->in_free = 0;
00169               }
00170 #if ZEND_DEBUG
00171               /* Shut-up the streams, they don't know what they are doing */
00172               net->stream->__exposed = 1;
00173 #endif
00174               efree(hashed_details);
00175        }
00176        /*
00177          Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
00178          be registered as resource (in EG(regular_list). So far, so good. However, it won't be
00179          unregistered till the script ends. So, we need to take care of that.
00180        */
00181        net->stream->in_free = 1;
00182        zend_hash_index_del(&EG(regular_list), net->stream->rsrc_id);
00183        net->stream->in_free = 0;
00184 
00185        if (!net->options.timeout_read) {
00186               /* should always happen because read_timeout cannot be set via API */
00187               net->options.timeout_read = (unsigned int) MYSQLND_G(net_read_timeout);
00188        }
00189        if (net->options.timeout_read) {
00190               DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read);
00191               tv.tv_sec = net->options.timeout_read;
00192               tv.tv_usec = 0;
00193               php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
00194        }
00195 
00196        if (!memcmp(scheme, "tcp://", sizeof("tcp://") - 1)) {
00197               /* TCP -> Set TCP_NODELAY */
00198               mysqlnd_set_sock_no_delay(net->stream TSRMLS_CC);
00199        }
00200 
00201        {
00202               unsigned int buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
00203               net->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC);
00204        }
00205 
00206 
00207        DBG_RETURN(PASS);
00208 }
00209 /* }}} */
00210 
00211 
00212 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
00213 #define COPY_HEADER(T,A)  do { \
00214               *(((char *)(T)))   = *(((char *)(A)));\
00215               *(((char *)(T))+1) = *(((char *)(A))+1);\
00216               *(((char *)(T))+2) = *(((char *)(A))+2);\
00217               *(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
00218 #define STORE_HEADER_SIZE(safe_storage, buffer)  COPY_HEADER((safe_storage), (buffer))
00219 #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
00220 
00221 /* {{{ mysqlnd_net::send */
00222 /*
00223   IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!!
00224                        This is done for performance reasons in the caller of this function.
00225                        Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
00226                        Neither are quick, thus the clients of this function are obligated to do
00227                        what they are asked for.
00228 
00229   `count` is actually the length of the payload data. Thus :
00230   count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer)
00231 */
00232 size_t
00233 MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC)
00234 {
00235        zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
00236        zend_uchar *safe_storage = safe_buf;
00237        MYSQLND_NET *net = conn->net;
00238        size_t old_chunk_size = net->stream->chunk_size;
00239        size_t ret, packets_sent = 1;
00240        size_t left = count;
00241        zend_uchar *p = (zend_uchar *) buf;
00242        zend_uchar * compress_buf = NULL;
00243        size_t to_be_sent;
00244 
00245        DBG_ENTER("mysqlnd_net::send");
00246        DBG_INF_FMT("conn=%llu count=%lu compression=%u", conn->thread_id, count, net->compressed);
00247 
00248        net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
00249 
00250        if (net->compressed == TRUE) {
00251               size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
00252               DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
00253               compress_buf = mnd_emalloc(comp_buf_size);
00254        }
00255 
00256        do {
00257               to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
00258 #ifdef MYSQLND_COMPRESSION_ENABLED
00259               if (net->compressed == TRUE) {
00260                      /* here we need to compress the data and then write it, first comes the compressed header */
00261                      size_t tmp_complen = to_be_sent;
00262                      size_t payload_size;
00263                      zend_uchar * uncompressed_payload = p; /* should include the header */
00264 
00265                      STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
00266                      int3store(uncompressed_payload, to_be_sent);
00267                      int1store(uncompressed_payload + 3, net->packet_no);
00268                      if (PASS == net->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
00269                                                                   uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC))
00270                      {
00271                             int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
00272                             payload_size = tmp_complen;
00273                      } else {
00274                             int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
00275                             memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
00276                             payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
00277                      }
00278                      RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
00279 
00280                      int3store(compress_buf, payload_size);
00281                      int1store(compress_buf + 3, net->packet_no);
00282                      DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
00283                      ret = conn->net->m.network_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
00284                      net->compressed_envelope_packet_no++;
00285   #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
00286                      if (res == Z_OK) {
00287                             size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
00288                             zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
00289                             int error = net->m.decode(decompressed_data, decompressed_size,
00290                                                                         compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
00291                             if (error == Z_OK) {
00292                                    int i;
00293                                    DBG_INF("success decompressing");
00294                                    for (i = 0 ; i < decompressed_size; i++) {
00295                                           if (i && (i % 30 == 0)) {
00296                                                  printf("\n\t\t");
00297                                           }
00298                                           printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
00299                                           DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
00300                                    }
00301                             } else {
00302                                    DBG_INF("error decompressing");
00303                             }
00304                             mnd_free(decompressed_data);
00305                      }
00306   #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
00307               } else
00308 #endif /* MYSQLND_COMPRESSION_ENABLED */
00309               {
00310                      DBG_INF("no compression");
00311                      STORE_HEADER_SIZE(safe_storage, p);
00312                      int3store(p, to_be_sent);
00313                      int1store(p + 3, net->packet_no);
00314                      ret = conn->net->m.network_write(conn, p, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC);
00315                      RESTORE_HEADER_SIZE(p, safe_storage);
00316                      net->compressed_envelope_packet_no++;
00317               }
00318               net->packet_no++;
00319 
00320               p += to_be_sent;
00321               left -= to_be_sent;
00322               packets_sent++;
00323               /*
00324                 if left is 0 then there is nothing more to send, but if the last packet was exactly 
00325                 with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
00326                 empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
00327                 indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
00328                 packet will be sent and this loop will end.
00329               */
00330        } while (ret && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
00331 
00332        DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no);
00333        /* Even for zero size payload we have to send a packet */
00334        if (!ret) {
00335               DBG_ERR_FMT("Can't %u send bytes", count);
00336               conn->state = CONN_QUIT_SENT;
00337               SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
00338        }
00339 
00340        MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn->stats,
00341                      STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
00342                      STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
00343                      STAT_PACKETS_SENT, packets_sent);
00344 
00345        net->stream->chunk_size = old_chunk_size;
00346        if (compress_buf) {
00347               mnd_efree(compress_buf);
00348        }
00349        DBG_RETURN(ret);
00350 }
00351 /* }}} */
00352 
00353 
00354 #ifdef MYSQLND_COMPRESSION_ENABLED
00355 /* {{{ php_mysqlnd_read_buffer_is_empty */
00356 static zend_bool
00357 php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
00358 {
00359        return buffer->len? FALSE:TRUE;
00360 }
00361 /* }}} */
00362 
00363 
00364 /* {{{ php_mysqlnd_read_buffer_read */
00365 static void
00366 php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
00367 {
00368        if (buffer->len >= count) {
00369               memcpy(dest, buffer->data + buffer->offset, count);
00370               buffer->offset += count;
00371               buffer->len -= count;
00372        }
00373 }
00374 /* }}} */
00375 
00376 
00377 /* {{{ php_mysqlnd_read_buffer_bytes_left */
00378 static size_t
00379 php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
00380 {
00381        return buffer->len;
00382 }
00383 /* }}} */
00384 
00385 
00386 /* {{{ php_mysqlnd_read_buffer_free */
00387 static void
00388 php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
00389 {
00390        DBG_ENTER("php_mysqlnd_read_buffer_free");
00391        if (*buffer) {
00392               mnd_efree((*buffer)->data);
00393               mnd_efree(*buffer);
00394               *buffer = NULL;
00395        }
00396        DBG_VOID_RETURN;
00397 }
00398 /* }}} */
00399 
00400 
00401 /* {{{ php_mysqlnd_create_read_buffer */
00402 static MYSQLND_READ_BUFFER *
00403 mysqlnd_create_read_buffer(size_t count TSRMLS_DC)
00404 {
00405        MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
00406        DBG_ENTER("mysqlnd_create_read_buffer");
00407        ret->is_empty = php_mysqlnd_read_buffer_is_empty;
00408        ret->read = php_mysqlnd_read_buffer_read;
00409        ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
00410        ret->free_buffer = php_mysqlnd_read_buffer_free;
00411        ret->data = mnd_emalloc(count);
00412        ret->size = ret->len = count;
00413        ret->offset = 0;
00414        DBG_RETURN(ret);
00415 }
00416 /* }}} */
00417 
00418 
00419 /* {{{ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer */
00420 static enum_func_status
00421 mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, size_t net_payload_size TSRMLS_DC)
00422 {
00423        MYSQLND_NET * net = conn->net;
00424        size_t decompressed_size;
00425        enum_func_status ret = PASS;
00426        zend_uchar * compressed_data = NULL;
00427        zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
00428        DBG_ENTER("mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer");
00429 
00430        /* Read the compressed header */
00431        if (FAIL == conn->net->m.network_read(conn, comp_header, COMPRESSED_HEADER_SIZE TSRMLS_CC)) {
00432               DBG_RETURN(FAIL);
00433        }
00434        decompressed_size = uint3korr(comp_header);
00435 
00436        /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
00437        /* we need to decompress the data */
00438 
00439        if (decompressed_size) {
00440               compressed_data = mnd_emalloc(net_payload_size);
00441               if (FAIL == conn->net->m.network_read(conn, compressed_data, net_payload_size TSRMLS_CC)) {
00442                      ret = FAIL;
00443                      goto end;
00444               }
00445               net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC);
00446               ret = net->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size TSRMLS_CC);
00447               if (ret == FAIL) {
00448                      goto end;
00449               }
00450        } else {
00451               DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
00452               net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC);
00453               if (FAIL == conn->net->m.network_read(conn, net->uncompressed_data->data, net_payload_size TSRMLS_CC)) {
00454                      ret = FAIL;
00455                      goto end;
00456               }
00457        }
00458 end:
00459        if (compressed_data) {
00460               mnd_efree(compressed_data);
00461        }
00462        DBG_RETURN(ret);
00463 }
00464 /* }}} */
00465 #endif /* MYSQLND_COMPRESSION_ENABLED */
00466 
00467 
00468 /* {{{ mysqlnd_net::decode */
00469 static enum_func_status
00470 MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, size_t uncompressed_data_len,
00471                                                                const zend_uchar * const compressed_data, size_t compressed_data_len TSRMLS_DC)
00472 {
00473 #ifdef MYSQLND_COMPRESSION_ENABLED
00474        int error;
00475        uLongf tmp_complen = uncompressed_data_len;
00476        DBG_ENTER("mysqlnd_net::decode");
00477        error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
00478 
00479        DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
00480        if (error != Z_OK) {
00481               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);
00482        }
00483        DBG_RETURN(error == Z_OK? PASS:FAIL);
00484 #else
00485        DBG_ENTER("mysqlnd_net::decode");
00486        DBG_RETURN(FAIL);
00487 #endif
00488 }
00489 /* }}} */
00490 
00491 
00492 /* {{{ mysqlnd_net::encode */
00493 static enum_func_status
00494 MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
00495                                                                const zend_uchar * const uncompressed_data, size_t uncompressed_data_len TSRMLS_DC)
00496 {
00497 #ifdef MYSQLND_COMPRESSION_ENABLED
00498        int error;
00499        uLongf tmp_complen = *compress_buffer_len;
00500        DBG_ENTER("mysqlnd_net::encode");
00501        error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
00502 
00503        if (error != Z_OK) {
00504               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);
00505        } else {
00506               *compress_buffer_len = tmp_complen;
00507               DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
00508        }
00509        
00510        DBG_RETURN(error == Z_OK? PASS:FAIL);
00511 #else
00512        DBG_ENTER("mysqlnd_net::encode");
00513        DBG_RETURN(FAIL);
00514 #endif
00515 }
00516 /* }}} */
00517 
00518 
00519 /* {{{ mysqlnd_net::receive */
00520 static enum_func_status
00521 MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
00522 {
00523        size_t to_read = count;
00524        zend_uchar * p = buffer;
00525        MYSQLND_NET * net = conn->net;
00526 
00527        DBG_ENTER("mysqlnd_net::receive");
00528 #ifdef MYSQLND_COMPRESSION_ENABLED
00529        if (net->compressed) {
00530               if (net->uncompressed_data) {
00531                      size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
00532                      DBG_INF_FMT("reading %u from uncompressed_data buffer", to_read_from_buffer);
00533                      if (to_read_from_buffer) {
00534                             net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
00535                             p += to_read_from_buffer;
00536                             to_read -= to_read_from_buffer;
00537                      }
00538                      DBG_INF_FMT("left %u to read", to_read);
00539                      if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
00540                             /* Everything was consumed. This should never happen here, but for security */
00541                             net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
00542                      }
00543               }
00544               if (to_read) {
00545                      zend_uchar net_header[MYSQLND_HEADER_SIZE];
00546                      size_t net_payload_size;
00547                      zend_uchar packet_no;
00548 
00549                      if (FAIL == net->m.network_read(conn, net_header, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
00550                             DBG_RETURN(FAIL);
00551                      }
00552                      net_payload_size = uint3korr(net_header);
00553                      packet_no = uint1korr(net_header + 3);
00554                      if (net->compressed_envelope_packet_no != packet_no) {
00555                             DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
00556                                                  net->compressed_envelope_packet_no, packet_no, net_payload_size);
00557 
00558                             php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
00559                                             net->compressed_envelope_packet_no, packet_no, net_payload_size);
00560                             DBG_RETURN(FAIL);
00561                      }
00562                      net->compressed_envelope_packet_no++;
00563 #ifdef MYSQLND_DUMP_HEADER_N_BODY
00564                      DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (unsigned long) net_payload_size);
00565 #endif
00566                      /* Now let's read from the wire, decompress it and fill the read buffer */
00567                      mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(conn, net_payload_size TSRMLS_CC);
00568 
00569                      /*
00570                        Now a bit of recursion - read from the read buffer,
00571                        if the data which we have just read from the wire
00572                        is not enough, then the recursive call will try to
00573                        satisfy it until it is satisfied.
00574                      */
00575                      DBG_RETURN(net->m.receive(conn, p, to_read TSRMLS_CC));
00576               }
00577               DBG_RETURN(PASS);
00578        }
00579 #endif /* MYSQLND_COMPRESSION_ENABLED */
00580        DBG_RETURN(net->m.network_read(conn, p, to_read TSRMLS_CC));
00581 }
00582 /* }}} */
00583 
00584 
00585 /* {{{ mysqlnd_net::set_client_option */
00586 static enum_func_status
00587 MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mysqlnd_option option, const char * const value TSRMLS_DC)
00588 {
00589        DBG_ENTER("mysqlnd_net::set_client_option");
00590        DBG_INF_FMT("option=%u", option);
00591        switch (option) {
00592               case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
00593                      DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
00594                      if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
00595                             DBG_RETURN(FAIL);
00596                      }
00597                      net->cmd_buffer.length = *(unsigned int*) value;
00598                      DBG_INF_FMT("new_length=%u", net->cmd_buffer.length);
00599                      if (!net->cmd_buffer.buffer) {
00600                             net->cmd_buffer.buffer = mnd_pemalloc(net->cmd_buffer.length, net->persistent);
00601                      } else {
00602                             net->cmd_buffer.buffer = mnd_perealloc(net->cmd_buffer.buffer, net->cmd_buffer.length, net->persistent);
00603                      }
00604                      break;
00605               case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
00606                      DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
00607                      net->options.net_read_buffer_size = *(unsigned int*) value;
00608                      DBG_INF_FMT("new_length=%u", net->options.net_read_buffer_size);
00609                      break;
00610               case MYSQL_OPT_CONNECT_TIMEOUT:
00611                      DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
00612                      net->options.timeout_connect = *(unsigned int*) value;
00613                      break;
00614               case MYSQLND_OPT_SSL_KEY:
00615                      {
00616                             zend_bool pers = net->persistent;
00617                             if (net->options.ssl_key) {
00618                                    mnd_pefree(net->options.ssl_key, pers);
00619                             }
00620                             net->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
00621                             break;
00622                      }
00623               case MYSQLND_OPT_SSL_CERT:
00624                      {
00625                             zend_bool pers = net->persistent;
00626                             if (net->options.ssl_cert) {
00627                                    mnd_pefree(net->options.ssl_cert, pers);
00628                             }
00629                             net->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
00630                             break;
00631                      }
00632               case MYSQLND_OPT_SSL_CA:
00633                      {
00634                             zend_bool pers = net->persistent;
00635                             if (net->options.ssl_ca) {
00636                                    mnd_pefree(net->options.ssl_ca, pers);
00637                             }
00638                             net->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
00639                             break;
00640                      }
00641               case MYSQLND_OPT_SSL_CAPATH:
00642                      {
00643                             zend_bool pers = net->persistent;
00644                             if (net->options.ssl_capath) {
00645                                    mnd_pefree(net->options.ssl_capath, pers);
00646                             }
00647                             net->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
00648                             break;
00649                      }
00650               case MYSQLND_OPT_SSL_CIPHER:
00651                      {
00652                             zend_bool pers = net->persistent;
00653                             if (net->options.ssl_cipher) {
00654                                    mnd_pefree(net->options.ssl_cipher, pers);
00655                             }
00656                             net->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
00657                             break;
00658                      }
00659               case MYSQLND_OPT_SSL_PASSPHRASE:
00660                      {
00661                             zend_bool pers = net->persistent;
00662                             if (net->options.ssl_passphrase) {
00663                                    mnd_pefree(net->options.ssl_passphrase, pers);
00664                             }
00665                             net->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
00666                             break;
00667                      }
00668               case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
00669                      net->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE;
00670                      break;
00671 #ifdef WHEN_SUPPORTED_BY_MYSQLI
00672               case MYSQL_OPT_READ_TIMEOUT:
00673                      DBG_INF("MYSQL_OPT_READ_TIMEOUT");
00674                      net->options.timeout_read = *(unsigned int*) value;
00675                      break;
00676               case MYSQL_OPT_WRITE_TIMEOUT:
00677                      DBG_INF("MYSQL_OPT_WRITE_TIMEOUT");
00678                      net->options.timeout_write = *(unsigned int*) value;
00679                      break;
00680 #endif
00681               case MYSQL_OPT_COMPRESS:
00682                      net->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION;
00683                      break;
00684               default:
00685                      DBG_RETURN(FAIL);
00686        }
00687        DBG_RETURN(PASS);
00688 }
00689 /* }}} */
00690 
00691 /* {{{ mysqlnd_net::consume_uneaten_data */
00692 size_t 
00693 MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC)
00694 {
00695 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
00696        /*
00697          Switch to non-blocking mode and try to consume something from
00698          the line, if possible, then continue. This saves us from looking for
00699          the actuall place where out-of-order packets have been sent.
00700          If someone is completely sure that everything is fine, he can switch it
00701          off.
00702        */
00703        char tmp_buf[256];
00704        size_t skipped_bytes = 0;
00705        int opt = PHP_STREAM_OPTION_BLOCKING;
00706        int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC);
00707 
00708        DBG_ENTER("mysqlnd_net::consume_uneaten_data");
00709 
00710        if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
00711               /* Do a read of 1 byte */
00712               int bytes_consumed;
00713 
00714               do {
00715                      skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf)));
00716               } while (bytes_consumed == sizeof(tmp_buf));
00717 
00718               if (was_blocked) {
00719                      net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC);
00720               }
00721 
00722               if (bytes_consumed) {
00723                      DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
00724                                           bytes_consumed, mysqlnd_command_to_text[net->last_command]);
00725                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
00726                                                   "consumed all the output from the server",
00727                                                   bytes_consumed, mysqlnd_command_to_text[net->last_command]);
00728               }
00729        }
00730        net->last_command = cmd;
00731 
00732        DBG_RETURN(skipped_bytes);
00733 #else
00734        return 0;
00735 #endif
00736 }
00737 /* }}} */
00738 
00739 /*
00740   in libmyusql, if cert and !key then key=cert
00741 */
00742 /* {{{ mysqlnd_net::enable_ssl */
00743 static enum_func_status
00744 MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
00745 {
00746 #ifdef MYSQLND_SSL_SUPPORTED
00747        php_stream_context *context = php_stream_context_alloc();
00748        DBG_ENTER("mysqlnd_net::enable_ssl");
00749        if (!context) {
00750               DBG_RETURN(FAIL);
00751        }
00752 
00753        if (net->options.ssl_key) {
00754               zval key_zval;
00755               ZVAL_STRING(&key_zval, net->options.ssl_key, 0);
00756               DBG_INF("key");
00757               php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
00758        }
00759        if (net->options.ssl_verify_peer) {
00760               zval verify_peer_zval;
00761               ZVAL_TRUE(&verify_peer_zval);
00762               DBG_INF("verify peer");
00763               php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
00764        }
00765        if (net->options.ssl_cert) {
00766               zval cert_zval;
00767               ZVAL_STRING(&cert_zval, net->options.ssl_cert, 0);
00768               DBG_INF_FMT("local_cert=%s", net->options.ssl_cert);
00769               php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
00770               if (!net->options.ssl_key) {
00771                      php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
00772               }
00773        }
00774        if (net->options.ssl_ca) {
00775               zval cafile_zval;
00776               ZVAL_STRING(&cafile_zval, net->options.ssl_ca, 0);
00777               DBG_INF_FMT("cafile=%s", net->options.ssl_ca);
00778               php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
00779        }
00780        if (net->options.ssl_capath) {
00781               zval capath_zval;
00782               ZVAL_STRING(&capath_zval, net->options.ssl_capath, 0);
00783               DBG_INF_FMT("capath=%s", net->options.ssl_capath);
00784               php_stream_context_set_option(context, "ssl", "cafile", &capath_zval);
00785        }
00786        if (net->options.ssl_passphrase) {
00787               zval passphrase_zval;
00788               ZVAL_STRING(&passphrase_zval, net->options.ssl_passphrase, 0);
00789               php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
00790        }
00791        if (net->options.ssl_cipher) {
00792               zval cipher_zval;
00793               ZVAL_STRING(&cipher_zval, net->options.ssl_cipher, 0);
00794               DBG_INF_FMT("ciphers=%s", net->options.ssl_cipher);
00795               php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
00796        }
00797        php_stream_context_set(net->stream, context);
00798        if (php_stream_xport_crypto_setup(net->stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL TSRMLS_CC) < 0 ||
00799            php_stream_xport_crypto_enable(net->stream, 1 TSRMLS_CC) < 0)
00800        {
00801               DBG_ERR("Cannot connect to MySQL by using SSL");
00802               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot connect to MySQL by using SSL");
00803               DBG_RETURN(FAIL);
00804        }
00805        /*
00806          get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
00807          then the context would not survive cleaning of EG(regular_list), where it is registered, as a
00808          resource. What happens is that after this destruction any use of the network will mean usage
00809          of the context, which means usage of already freed memory, bad. Actually we don't need this
00810          context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
00811        */
00812        php_stream_context_set(net->stream, NULL);
00813 
00814        if (net->options.timeout_read) {
00815               struct timeval tv;
00816               DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->options.timeout_read);
00817               tv.tv_sec = net->options.timeout_read;
00818               tv.tv_usec = 0;
00819               php_stream_set_option(net->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
00820        }
00821 
00822        DBG_RETURN(PASS);
00823 #else
00824        DBG_ENTER("mysqlnd_net::enable_ssl");
00825        DBG_RETURN(PASS);
00826 #endif
00827 }
00828 /* }}} */
00829 
00830 
00831 /* {{{ mysqlnd_net::disable_ssl */
00832 static enum_func_status
00833 MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
00834 {
00835        DBG_ENTER("mysqlnd_net::disable_ssl");
00836        DBG_RETURN(PASS);
00837 }
00838 /* }}} */
00839 
00840 
00841 /* {{{ mysqlnd_net::set_client_option */
00842 static void
00843 MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC)
00844 {
00845        zend_bool pers = net->persistent;
00846        DBG_ENTER("mysqlnd_net::free_contents");
00847 
00848 #ifdef MYSQLND_COMPRESSION_ENABLED
00849        if (net->uncompressed_data) {
00850               net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
00851        }
00852 #endif
00853        if (net->options.ssl_key) {
00854               mnd_pefree(net->options.ssl_key, pers);
00855               net->options.ssl_key = NULL;
00856        }
00857        if (net->options.ssl_cert) {
00858               mnd_pefree(net->options.ssl_cert, pers);
00859               net->options.ssl_cert = NULL;
00860        }
00861        if (net->options.ssl_ca) {
00862               mnd_pefree(net->options.ssl_ca, pers);
00863               net->options.ssl_ca = NULL;
00864        }
00865        if (net->options.ssl_capath) {
00866               mnd_pefree(net->options.ssl_capath, pers);
00867               net->options.ssl_capath = NULL;
00868        }
00869        if (net->options.ssl_cipher) {
00870               mnd_pefree(net->options.ssl_cipher, pers);
00871               net->options.ssl_cipher = NULL;
00872        }
00873 
00874        DBG_VOID_RETURN;
00875 }
00876 /* }}} */
00877 
00878 static 
00879 MYSQLND_CLASS_METHODS_START(mysqlnd_net)
00880        MYSQLND_METHOD(mysqlnd_net, connect),
00881        MYSQLND_METHOD(mysqlnd_net, send),
00882        MYSQLND_METHOD(mysqlnd_net, receive),
00883        MYSQLND_METHOD(mysqlnd_net, set_client_option),
00884        MYSQLND_METHOD(mysqlnd_net, network_read),
00885        MYSQLND_METHOD(mysqlnd_net, network_write),
00886        MYSQLND_METHOD(mysqlnd_net, decode),
00887        MYSQLND_METHOD(mysqlnd_net, encode),
00888        MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data),
00889        MYSQLND_METHOD(mysqlnd_net, free_contents),
00890        MYSQLND_METHOD(mysqlnd_net, enable_ssl),
00891        MYSQLND_METHOD(mysqlnd_net, disable_ssl)
00892 MYSQLND_CLASS_METHODS_END;
00893 
00894 
00895 /* {{{ mysqlnd_net_init */
00896 PHPAPI MYSQLND_NET *
00897 mysqlnd_net_init(zend_bool persistent TSRMLS_DC)
00898 {
00899        size_t alloc_size = sizeof(MYSQLND_NET) + mysqlnd_plugin_count() * sizeof(void *);
00900        MYSQLND_NET * net = mnd_pecalloc(1, alloc_size, persistent);
00901 
00902        DBG_ENTER("mysqlnd_net_init");
00903        DBG_INF_FMT("persistent=%u", persistent);
00904        if (net) {
00905               net->persistent = persistent;
00906               net->m = mysqlnd_mysqlnd_net_methods;
00907 
00908               {
00909                      unsigned int buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
00910                      net->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC);
00911               }
00912        }
00913        DBG_RETURN(net);
00914 }
00915 /* }}} */
00916 
00917 
00918 /* {{{ mysqlnd_net_free */
00919 PHPAPI void
00920 mysqlnd_net_free(MYSQLND_NET * const net TSRMLS_DC)
00921 {
00922        DBG_ENTER("mysqlnd_net_free");
00923 
00924        if (net) {
00925               zend_bool pers = net->persistent;
00926 
00927               net->m.free_contents(net TSRMLS_CC);
00928               if (net->cmd_buffer.buffer) {
00929                      DBG_INF("Freeing cmd buffer");
00930                      mnd_pefree(net->cmd_buffer.buffer, pers);
00931                      net->cmd_buffer.buffer = NULL;
00932               }
00933               if (net->stream) {
00934                      DBG_INF_FMT("Freeing stream. abstract=%p", net->stream->abstract);
00935                      if (pers) {
00936                             php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
00937                      } else {
00938                             php_stream_free(net->stream, PHP_STREAM_FREE_CLOSE);
00939                      }
00940                      net->stream = NULL;
00941               }
00942               mnd_pefree(net, pers);
00943        }
00944        DBG_VOID_RETURN;
00945 }
00946 /* }}} */
00947 
00948 
00949 /* {{{ _mysqlnd_plugin_get_plugin_net_data */
00950 PHPAPI void ** _mysqlnd_plugin_get_plugin_net_data(const MYSQLND_NET * net, unsigned int plugin_id TSRMLS_DC)
00951 {
00952        DBG_ENTER("_mysqlnd_plugin_get_plugin_net_data");
00953        DBG_INF_FMT("plugin_id=%u", plugin_id);
00954        if (!net || plugin_id >= mysqlnd_plugin_count()) {
00955               return NULL;
00956        }
00957        DBG_RETURN((void *)((char *)net + sizeof(MYSQLND_NET) + plugin_id * sizeof(void *)));
00958 }
00959 /* }}} */
00960 
00961 
00962 /* {{{ mysqlnd_net_get_methods */
00963 PHPAPI struct st_mysqlnd_net_methods *
00964 mysqlnd_net_get_methods()
00965 {
00966        return &mysqlnd_mysqlnd_net_methods;
00967 }
00968 /* }}} */
00969 
00970 
00971 /*
00972  * Local variables:
00973  * tab-width: 4
00974  * c-basic-offset: 4
00975  * End:
00976  * vim600: noet sw=4 ts=4 fdm=marker
00977  * vim<600: noet sw=4 ts=4
00978  */