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

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