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