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