root/ext/mysqlnd/mysqlnd_net.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mysqlnd_set_sock_no_delay
  2. mysqlnd_set_sock_keepalive
  3. php_mysqlnd_read_buffer_is_empty
  4. php_mysqlnd_read_buffer_read
  5. php_mysqlnd_read_buffer_bytes_left
  6. php_mysqlnd_read_buffer_free
  7. mysqlnd_create_read_buffer
  8. MYSQLND_METHOD
  9. MYSQLND_METHOD
  10. MYSQLND_METHOD
  11. MYSQLND_METHOD
  12. MYSQLND_CLASS_METHODS_START
  13. mysqlnd_net_free

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

/* [<][>][^][v][top][bottom][index][help] */