1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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@php.net> |
16 | Ulf Wendel <uw@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "php.h"
21 #include "mysqlnd.h"
22 #include "mysqlnd_priv.h"
23 #include "mysqlnd_statistics.h"
24 #include "mysqlnd_debug.h"
25 #include "mysqlnd_ext_plugin.h"
26 #include "php_network.h"
27
28 #ifndef PHP_WIN32
29 #include <netinet/tcp.h>
30 #else
31 #include <winsock.h>
32 #endif
33
34
35 /* {{{ mysqlnd_set_sock_no_delay */
36 static int
mysqlnd_set_sock_no_delay(php_stream * stream)37 mysqlnd_set_sock_no_delay(php_stream * stream)
38 {
39 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
40 int ret = SUCCESS;
41 int flag = 1;
42 int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
43
44 DBG_ENTER("mysqlnd_set_sock_no_delay");
45
46 if (result == -1) {
47 ret = FAILURE;
48 }
49
50 DBG_RETURN(ret);
51 }
52 /* }}} */
53
54
55 /* {{{ mysqlnd_set_sock_keepalive */
56 static int
mysqlnd_set_sock_keepalive(php_stream * stream)57 mysqlnd_set_sock_keepalive(php_stream * stream)
58 {
59 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
60 int ret = SUCCESS;
61 int flag = 1;
62 int result = setsockopt(socketd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int));
63
64 DBG_ENTER("mysqlnd_set_sock_keepalive");
65
66 if (result == -1) {
67 ret = FAILURE;
68 }
69
70 DBG_RETURN(ret);
71 }
72 /* }}} */
73
74
75 /* {{{ mysqlnd_vio::network_read */
76 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,network_read)77 MYSQLND_METHOD(mysqlnd_vio, network_read)(MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
78 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
79 {
80 enum_func_status return_value = PASS;
81 php_stream * net_stream = vio->data->m.get_stream(vio);
82 size_t to_read = count;
83 zend_uchar * p = buffer;
84
85 DBG_ENTER("mysqlnd_vio::network_read");
86 DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
87
88 while (to_read) {
89 ssize_t ret = php_stream_read(net_stream, (char *) p, to_read);
90 if (ret <= 0) {
91 DBG_ERR_FMT("Error while reading header from socket");
92 return_value = FAIL;
93 break;
94 }
95 p += ret;
96 to_read -= ret;
97 }
98 MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
99 DBG_RETURN(return_value);
100 }
101 /* }}} */
102
103
104 /* {{{ mysqlnd_vio::network_write */
105 static ssize_t
MYSQLND_METHOD(mysqlnd_vio,network_write)106 MYSQLND_METHOD(mysqlnd_vio, network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buffer, const size_t count,
107 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
108 {
109 ssize_t ret;
110 DBG_ENTER("mysqlnd_vio::network_write");
111 DBG_INF_FMT("sending %u bytes", count);
112 ret = php_stream_write(vio->data->m.get_stream(vio), (char *)buffer, count);
113 DBG_RETURN(ret);
114 }
115 /* }}} */
116
117
118 /* {{{ mysqlnd_vio::open_pipe */
119 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,open_pipe)120 MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
121 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
122 {
123 unsigned int streams_options = 0;
124 dtor_func_t origin_dtor;
125 php_stream * net_stream = NULL;
126
127 DBG_ENTER("mysqlnd_vio::open_pipe");
128 if (persistent) {
129 streams_options |= STREAM_OPEN_PERSISTENT;
130 }
131 streams_options |= IGNORE_URL;
132 net_stream = php_stream_open_wrapper(scheme.s + sizeof("pipe://") - 1, "r+", streams_options, NULL);
133 if (!net_stream) {
134 SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
135 DBG_RETURN(NULL);
136 }
137 /*
138 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
139 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
140 unregistered until the script ends. So, we need to take care of that.
141 */
142 origin_dtor = EG(regular_list).pDestructor;
143 EG(regular_list).pDestructor = NULL;
144 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
145 EG(regular_list).pDestructor = origin_dtor;
146 net_stream->res = NULL;
147
148 DBG_RETURN(net_stream);
149 }
150 /* }}} */
151
152
153 /* {{{ mysqlnd_vio::open_tcp_or_unix */
154 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,open_tcp_or_unix)155 MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
156 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
157 {
158 unsigned int streams_options = 0;
159 unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
160 char * hashed_details = NULL;
161 int hashed_details_len = 0;
162 zend_string *errstr = NULL;
163 int errcode = 0;
164 struct timeval tv;
165 dtor_func_t origin_dtor;
166 php_stream * net_stream = NULL;
167
168 DBG_ENTER("mysqlnd_vio::open_tcp_or_unix");
169
170 vio->data->stream = NULL;
171
172 if (persistent) {
173 hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", vio);
174 DBG_INF_FMT("hashed_details=%s", hashed_details);
175 }
176
177 if (vio->data->options.timeout_connect) {
178 tv.tv_sec = vio->data->options.timeout_connect;
179 tv.tv_usec = 0;
180 }
181
182 DBG_INF_FMT("calling php_stream_xport_create");
183 net_stream = php_stream_xport_create(scheme.s, scheme.l, streams_options, streams_flags,
184 hashed_details, (vio->data->options.timeout_connect) ? &tv : NULL,
185 NULL /*ctx*/, &errstr, &errcode);
186 if (errstr || !net_stream) {
187 DBG_ERR("Error");
188 if (hashed_details) {
189 mnd_sprintf_free(hashed_details);
190 }
191 errcode = CR_CONNECTION_ERROR;
192 SET_CLIENT_ERROR(error_info,
193 CR_CONNECTION_ERROR,
194 UNKNOWN_SQLSTATE,
195 errstr? ZSTR_VAL(errstr):"Unknown error while connecting");
196 if (errstr) {
197 zend_string_release_ex(errstr, 0);
198 }
199 DBG_RETURN(NULL);
200 }
201 if (hashed_details) {
202 /*
203 If persistent, the streams register it in EG(persistent_list).
204 This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
205 whatever they have to.
206 */
207 zend_resource *le;
208
209 if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
210 origin_dtor = EG(persistent_list).pDestructor;
211 /*
212 in_free will let streams code skip destructing - big HACK,
213 but STREAMS suck big time regarding persistent streams.
214 Just not compatible for extensions that need persistency.
215 */
216 EG(persistent_list).pDestructor = NULL;
217 zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
218 EG(persistent_list).pDestructor = origin_dtor;
219 pefree(le, 1);
220 }
221 #if ZEND_DEBUG
222 /* Shut-up the streams, they don't know what they are doing */
223 net_stream->__exposed = 1;
224 #endif
225 mnd_sprintf_free(hashed_details);
226 }
227
228 /*
229 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
230 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
231 unregistered until the script ends. So, we need to take care of that.
232 */
233 origin_dtor = EG(regular_list).pDestructor;
234 EG(regular_list).pDestructor = NULL;
235 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
236 efree(net_stream->res);
237 net_stream->res = NULL;
238 EG(regular_list).pDestructor = origin_dtor;
239 DBG_RETURN(net_stream);
240 }
241 /* }}} */
242
243
244 /* {{{ mysqlnd_vio::post_connect_set_opt */
245 static void
MYSQLND_METHOD(mysqlnd_vio,post_connect_set_opt)246 MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
247 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
248 {
249 php_stream * net_stream = vio->data->m.get_stream(vio);
250 DBG_ENTER("mysqlnd_vio::post_connect_set_opt");
251 if (net_stream) {
252 if (vio->data->options.timeout_read) {
253 struct timeval tv;
254 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", vio->data->options.timeout_read);
255 tv.tv_sec = vio->data->options.timeout_read;
256 tv.tv_usec = 0;
257 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
258 }
259
260 if (!memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1)) {
261 /* TCP -> Set TCP_NODELAY */
262 mysqlnd_set_sock_no_delay(net_stream);
263 /* TCP -> Set SO_KEEPALIVE */
264 mysqlnd_set_sock_keepalive(net_stream);
265 }
266
267 net_stream->chunk_size = vio->data->options.net_read_buffer_size;
268 }
269
270 DBG_VOID_RETURN;
271 }
272 /* }}} */
273
274
275 /* {{{ mysqlnd_vio::get_open_stream */
276 static func_mysqlnd_vio__open_stream
MYSQLND_METHOD(mysqlnd_vio,get_open_stream)277 MYSQLND_METHOD(mysqlnd_vio, get_open_stream)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
278 MYSQLND_ERROR_INFO * const error_info)
279 {
280 func_mysqlnd_vio__open_stream ret = NULL;
281 DBG_ENTER("mysqlnd_vio::get_open_stream");
282 if (scheme.l > (sizeof("pipe://") - 1) && !memcmp(scheme.s, "pipe://", sizeof("pipe://") - 1)) {
283 ret = vio->data->m.open_pipe;
284 } else if ((scheme.l > (sizeof("tcp://") - 1) && !memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1))
285 ||
286 (scheme.l > (sizeof("unix://") - 1) && !memcmp(scheme.s, "unix://", sizeof("unix://") - 1)))
287 {
288 ret = vio->data->m.open_tcp_or_unix;
289 }
290
291 if (!ret) {
292 SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "No handler for this scheme");
293 }
294
295 DBG_RETURN(ret);
296 }
297 /* }}} */
298
299
300 /* {{{ mysqlnd_vio::connect */
301 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,connect)302 MYSQLND_METHOD(mysqlnd_vio, connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
303 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
304 {
305 enum_func_status ret = FAIL;
306 func_mysqlnd_vio__open_stream open_stream = NULL;
307 DBG_ENTER("mysqlnd_vio::connect");
308
309 vio->data->m.close_stream(vio, conn_stats, error_info);
310
311 open_stream = vio->data->m.get_open_stream(vio, scheme, error_info);
312 if (open_stream) {
313 php_stream * net_stream = open_stream(vio, scheme, persistent, conn_stats, error_info);
314 if (net_stream && PASS == vio->data->m.set_stream(vio, net_stream)) {
315 vio->data->m.post_connect_set_opt(vio, scheme, conn_stats, error_info);
316 ret = PASS;
317 }
318 }
319
320 DBG_RETURN(ret);
321 }
322 /* }}} */
323
324
325 /* {{{ mysqlnd_vio::set_client_option */
326 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,set_client_option)327 MYSQLND_METHOD(mysqlnd_vio, set_client_option)(MYSQLND_VIO * const net, enum_mysqlnd_client_option option, const char * const value)
328 {
329 DBG_ENTER("mysqlnd_vio::set_client_option");
330 DBG_INF_FMT("option=%u", option);
331 switch (option) {
332 case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
333 DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
334 net->data->options.net_read_buffer_size = *(unsigned int*) value;
335 DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->data->options.net_read_buffer_size);
336 break;
337 case MYSQL_OPT_CONNECT_TIMEOUT:
338 DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
339 net->data->options.timeout_connect = *(unsigned int*) value;
340 break;
341 case MYSQLND_OPT_SSL_KEY:
342 {
343 zend_bool pers = net->persistent;
344 if (net->data->options.ssl_key) {
345 mnd_pefree(net->data->options.ssl_key, pers);
346 }
347 net->data->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
348 break;
349 }
350 case MYSQLND_OPT_SSL_CERT:
351 {
352 zend_bool pers = net->persistent;
353 if (net->data->options.ssl_cert) {
354 mnd_pefree(net->data->options.ssl_cert, pers);
355 }
356 net->data->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
357 break;
358 }
359 case MYSQLND_OPT_SSL_CA:
360 {
361 zend_bool pers = net->persistent;
362 if (net->data->options.ssl_ca) {
363 mnd_pefree(net->data->options.ssl_ca, pers);
364 }
365 net->data->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
366 break;
367 }
368 case MYSQLND_OPT_SSL_CAPATH:
369 {
370 zend_bool pers = net->persistent;
371 if (net->data->options.ssl_capath) {
372 mnd_pefree(net->data->options.ssl_capath, pers);
373 }
374 net->data->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
375 break;
376 }
377 case MYSQLND_OPT_SSL_CIPHER:
378 {
379 zend_bool pers = net->persistent;
380 if (net->data->options.ssl_cipher) {
381 mnd_pefree(net->data->options.ssl_cipher, pers);
382 }
383 net->data->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
384 break;
385 }
386 case MYSQLND_OPT_SSL_PASSPHRASE:
387 {
388 zend_bool pers = net->persistent;
389 if (net->data->options.ssl_passphrase) {
390 mnd_pefree(net->data->options.ssl_passphrase, pers);
391 }
392 net->data->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
393 break;
394 }
395 case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
396 {
397 enum mysqlnd_ssl_peer val = *((enum mysqlnd_ssl_peer *)value);
398 switch (val) {
399 case MYSQLND_SSL_PEER_VERIFY:
400 DBG_INF("MYSQLND_SSL_PEER_VERIFY");
401 break;
402 case MYSQLND_SSL_PEER_DONT_VERIFY:
403 DBG_INF("MYSQLND_SSL_PEER_DONT_VERIFY");
404 break;
405 case MYSQLND_SSL_PEER_DEFAULT:
406 DBG_INF("MYSQLND_SSL_PEER_DEFAULT");
407 val = MYSQLND_SSL_PEER_DEFAULT;
408 break;
409 default:
410 DBG_INF("default = MYSQLND_SSL_PEER_DEFAULT_ACTION");
411 val = MYSQLND_SSL_PEER_DEFAULT;
412 break;
413 }
414 net->data->options.ssl_verify_peer = val;
415 break;
416 }
417 case MYSQL_OPT_READ_TIMEOUT:
418 net->data->options.timeout_read = *(unsigned int*) value;
419 break;
420 #ifdef WHEN_SUPPORTED_BY_MYSQLI
421 case MYSQL_OPT_WRITE_TIMEOUT:
422 net->data->options.timeout_write = *(unsigned int*) value;
423 break;
424 #endif
425 default:
426 DBG_RETURN(FAIL);
427 }
428 DBG_RETURN(PASS);
429 }
430 /* }}} */
431
432
433 /* {{{ mysqlnd_vio::consume_uneaten_data */
434 size_t
MYSQLND_METHOD(mysqlnd_vio,consume_uneaten_data)435 MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data)(MYSQLND_VIO * const net, enum php_mysqlnd_server_command cmd)
436 {
437 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
438 /*
439 Switch to non-blocking mode and try to consume something from
440 the line, if possible, then continue. This saves us from looking for
441 the actual place where out-of-order packets have been sent.
442 If someone is completely sure that everything is fine, he can switch it
443 off.
444 */
445 char tmp_buf[256];
446 size_t skipped_bytes = 0;
447 int opt = PHP_STREAM_OPTION_BLOCKING;
448 php_stream * net_stream = net->data->get_stream(net);
449 int was_blocked = net_stream->ops->set_option(net_stream, opt, 0, NULL);
450
451 DBG_ENTER("mysqlnd_vio::consume_uneaten_data");
452
453 if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
454 /* Do a read of 1 byte */
455 ssize_t bytes_consumed;
456
457 do {
458 bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf));
459 if (bytes_consumed <= 0) {
460 break;
461 }
462 skipped_bytes += bytes_consumed;
463 } while (bytes_consumed == sizeof(tmp_buf));
464
465 if (was_blocked) {
466 net_stream->ops->set_option(net_stream, opt, 1, NULL);
467 }
468
469 if (bytes_consumed) {
470 DBG_ERR_FMT("Skipped %u bytes. Last command hasn't consumed all the output from the server",
471 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
472 php_error_docref(NULL, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
473 "consumed all the output from the server",
474 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
475 }
476 }
477 net->last_command = cmd;
478
479 DBG_RETURN(skipped_bytes);
480 #else
481 return 0;
482 #endif
483 }
484 /* }}} */
485
486 /*
487 in libmyusql, if cert and !key then key=cert
488 */
489 /* {{{ mysqlnd_vio::enable_ssl */
490 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,enable_ssl)491 MYSQLND_METHOD(mysqlnd_vio, enable_ssl)(MYSQLND_VIO * const net)
492 {
493 #ifdef MYSQLND_SSL_SUPPORTED
494 php_stream_context * context = php_stream_context_alloc();
495 php_stream * net_stream = net->data->m.get_stream(net);
496 zend_bool any_flag = FALSE;
497
498 DBG_ENTER("mysqlnd_vio::enable_ssl");
499
500 if (net->data->options.ssl_key) {
501 zval key_zval;
502 ZVAL_STRING(&key_zval, net->data->options.ssl_key);
503 php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
504 zval_ptr_dtor(&key_zval);
505 any_flag = TRUE;
506 }
507 if (net->data->options.ssl_cert) {
508 zval cert_zval;
509 ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
510 php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
511 if (!net->data->options.ssl_key) {
512 php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
513 }
514 zval_ptr_dtor(&cert_zval);
515 any_flag = TRUE;
516 }
517 if (net->data->options.ssl_ca) {
518 zval cafile_zval;
519 ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
520 php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
521 zval_ptr_dtor(&cafile_zval);
522 any_flag = TRUE;
523 }
524 if (net->data->options.ssl_capath) {
525 zval capath_zval;
526 ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
527 php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
528 zval_ptr_dtor(&capath_zval);
529 any_flag = TRUE;
530 }
531 if (net->data->options.ssl_passphrase) {
532 zval passphrase_zval;
533 ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
534 php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
535 zval_ptr_dtor(&passphrase_zval);
536 any_flag = TRUE;
537 }
538 if (net->data->options.ssl_cipher) {
539 zval cipher_zval;
540 ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
541 php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
542 zval_ptr_dtor(&cipher_zval);
543 any_flag = TRUE;
544 }
545 {
546 zval verify_peer_zval;
547 zend_bool verify;
548
549 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
550 net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
551 }
552
553 verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
554
555 DBG_INF_FMT("VERIFY=%d", verify);
556 ZVAL_BOOL(&verify_peer_zval, verify);
557 php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
558 php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
559 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
560 ZVAL_TRUE(&verify_peer_zval);
561 php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
562 }
563 }
564 php_stream_context_set(net_stream, context);
565 if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
566 php_stream_xport_crypto_enable(net_stream, 1) < 0)
567 {
568 DBG_ERR("Cannot connect to MySQL by using SSL");
569 php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
570 DBG_RETURN(FAIL);
571 }
572 net->data->ssl = TRUE;
573 /*
574 get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
575 then the context would not survive cleaning of EG(regular_list), where it is registered, as a
576 resource. What happens is that after this destruction any use of the network will mean usage
577 of the context, which means usage of already freed memory, bad. Actually we don't need this
578 context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
579 */
580 php_stream_context_set(net_stream, NULL);
581
582 if (net->data->options.timeout_read) {
583 struct timeval tv;
584 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
585 tv.tv_sec = net->data->options.timeout_read;
586 tv.tv_usec = 0;
587 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
588 }
589
590 DBG_RETURN(PASS);
591 #else
592 DBG_ENTER("mysqlnd_vio::enable_ssl");
593 DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
594 DBG_RETURN(PASS);
595 #endif
596 }
597 /* }}} */
598
599
600 /* {{{ mysqlnd_vio::disable_ssl */
601 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,disable_ssl)602 MYSQLND_METHOD(mysqlnd_vio, disable_ssl)(MYSQLND_VIO * const vio)
603 {
604 DBG_ENTER("mysqlnd_vio::disable_ssl");
605 DBG_RETURN(PASS);
606 }
607 /* }}} */
608
609
610 /* {{{ mysqlnd_vio::free_contents */
611 static void
MYSQLND_METHOD(mysqlnd_vio,free_contents)612 MYSQLND_METHOD(mysqlnd_vio, free_contents)(MYSQLND_VIO * net)
613 {
614 zend_bool pers = net->persistent;
615 DBG_ENTER("mysqlnd_vio::free_contents");
616
617 if (net->data->options.ssl_key) {
618 mnd_pefree(net->data->options.ssl_key, pers);
619 net->data->options.ssl_key = NULL;
620 }
621 if (net->data->options.ssl_cert) {
622 mnd_pefree(net->data->options.ssl_cert, pers);
623 net->data->options.ssl_cert = NULL;
624 }
625 if (net->data->options.ssl_ca) {
626 mnd_pefree(net->data->options.ssl_ca, pers);
627 net->data->options.ssl_ca = NULL;
628 }
629 if (net->data->options.ssl_capath) {
630 mnd_pefree(net->data->options.ssl_capath, pers);
631 net->data->options.ssl_capath = NULL;
632 }
633 if (net->data->options.ssl_cipher) {
634 mnd_pefree(net->data->options.ssl_cipher, pers);
635 net->data->options.ssl_cipher = NULL;
636 }
637
638 DBG_VOID_RETURN;
639 }
640 /* }}} */
641
642
643 /* {{{ mysqlnd_vio::close_stream */
644 static void
MYSQLND_METHOD(mysqlnd_vio,close_stream)645 MYSQLND_METHOD(mysqlnd_vio, close_stream)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
646 {
647 php_stream * net_stream;
648 DBG_ENTER("mysqlnd_vio::close_stream");
649 if (net && (net_stream = net->data->m.get_stream(net))) {
650 zend_bool pers = net->persistent;
651 DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
652 /* We removed the resource from the stream, so pass FREE_RSRC_DTOR now to force
653 * destruction to occur during shutdown, because it won't happen through the resource. */
654 /* TODO: The EG(active) check here is dead -- check IN_SHUTDOWN? */
655 if (pers && EG(active)) {
656 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
657 } else {
658 /*
659 otherwise we will crash because the EG(persistent_list) has been freed already,
660 before the modules are shut down
661 */
662 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
663 }
664 net->data->m.set_stream(net, NULL);
665 }
666
667 DBG_VOID_RETURN;
668 }
669 /* }}} */
670
671
672 /* {{{ mysqlnd_vio::init */
673 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,init)674 MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
675 {
676 unsigned int buf_size;
677 DBG_ENTER("mysqlnd_vio::init");
678
679 buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
680 net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
681
682 buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
683 net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
684
685 DBG_RETURN(PASS);
686 }
687 /* }}} */
688
689
690 /* {{{ mysqlnd_vio::dtor */
691 static void
MYSQLND_METHOD(mysqlnd_vio,dtor)692 MYSQLND_METHOD(mysqlnd_vio, dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
693 {
694 DBG_ENTER("mysqlnd_vio::dtor");
695 if (vio) {
696 vio->data->m.free_contents(vio);
697 vio->data->m.close_stream(vio, stats, error_info);
698
699 mnd_pefree(vio, vio->persistent);
700 }
701 DBG_VOID_RETURN;
702 }
703 /* }}} */
704
705
706 /* {{{ mysqlnd_vio::get_stream */
707 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,get_stream)708 MYSQLND_METHOD(mysqlnd_vio, get_stream)(const MYSQLND_VIO * const net)
709 {
710 DBG_ENTER("mysqlnd_vio::get_stream");
711 DBG_INF_FMT("%p", net? net->data->stream:NULL);
712 DBG_RETURN(net? net->data->stream:NULL);
713 }
714 /* }}} */
715
716
717 /* {{{ mysqlnd_vio::set_stream */
718 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,set_stream)719 MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const vio, php_stream * net_stream)
720 {
721 DBG_ENTER("mysqlnd_vio::set_stream");
722 if (vio) {
723 vio->data->stream = net_stream;
724 DBG_RETURN(PASS);
725 }
726 DBG_RETURN(FAIL);
727 }
728 /* }}} */
729
730
731 /* {{{ mysqlnd_vio::has_valid_stream */
732 static zend_bool
MYSQLND_METHOD(mysqlnd_vio,has_valid_stream)733 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream)(const MYSQLND_VIO * const vio)
734 {
735 DBG_ENTER("mysqlnd_vio::has_valid_stream");
736 DBG_INF_FMT("%p %p", vio, vio? vio->data->stream:NULL);
737 DBG_RETURN((vio && vio->data->stream)? TRUE: FALSE);
738 }
739 /* }}} */
740
741
742 MYSQLND_CLASS_METHODS_START(mysqlnd_vio)
743 MYSQLND_METHOD(mysqlnd_vio, init),
744 MYSQLND_METHOD(mysqlnd_vio, dtor),
745
746 MYSQLND_METHOD(mysqlnd_vio, connect),
747
748 MYSQLND_METHOD(mysqlnd_vio, close_stream),
749 MYSQLND_METHOD(mysqlnd_vio, open_pipe),
750 MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix),
751
752 MYSQLND_METHOD(mysqlnd_vio, get_stream),
753 MYSQLND_METHOD(mysqlnd_vio, set_stream),
754 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream),
755 MYSQLND_METHOD(mysqlnd_vio, get_open_stream),
756
757 MYSQLND_METHOD(mysqlnd_vio, set_client_option),
758 MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt),
759
760 MYSQLND_METHOD(mysqlnd_vio, enable_ssl),
761 MYSQLND_METHOD(mysqlnd_vio, disable_ssl),
762
763 MYSQLND_METHOD(mysqlnd_vio, network_read),
764 MYSQLND_METHOD(mysqlnd_vio, network_write),
765
766 MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data),
767
768 MYSQLND_METHOD(mysqlnd_vio, free_contents),
769 MYSQLND_CLASS_METHODS_END;
770
771
772 /* {{{ mysqlnd_vio_init */
773 PHPAPI MYSQLND_VIO *
mysqlnd_vio_init(zend_bool persistent,MYSQLND_CLASS_METHODS_TYPE (mysqlnd_object_factory)* object_factory,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)774 mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
775 {
776 MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
777 MYSQLND_VIO * vio;
778 DBG_ENTER("mysqlnd_vio_init");
779 vio = factory->get_vio(persistent, stats, error_info);
780 DBG_RETURN(vio);
781 }
782 /* }}} */
783
784
785 /* {{{ mysqlnd_vio_free */
786 PHPAPI void
mysqlnd_vio_free(MYSQLND_VIO * const vio,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)787 mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
788 {
789 DBG_ENTER("mysqlnd_vio_free");
790 if (vio) {
791 vio->data->m.dtor(vio, stats, error_info);
792 }
793 DBG_VOID_RETURN;
794 }
795 /* }}} */
796