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