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 old_chunk_size = net_stream->chunk_size;
83 size_t to_read = count, ret;
84 zend_uchar * p = buffer;
85
86 DBG_ENTER("mysqlnd_vio::network_read");
87 DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
88
89 net_stream->chunk_size = MIN(to_read, vio->data->options.net_read_buffer_size);
90 while (to_read) {
91 if (!(ret = php_stream_read(net_stream, (char *) p, to_read))) {
92 DBG_ERR_FMT("Error while reading header from socket");
93 return_value = FAIL;
94 break;
95 }
96 p += ret;
97 to_read -= ret;
98 }
99 MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
100 net_stream->chunk_size = old_chunk_size;
101 DBG_RETURN(return_value);
102 }
103 /* }}} */
104
105
106 /* {{{ mysqlnd_vio::network_write */
107 static size_t
MYSQLND_METHOD(mysqlnd_vio,network_write)108 MYSQLND_METHOD(mysqlnd_vio, network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buffer, const size_t count,
109 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
110 {
111 size_t ret;
112 DBG_ENTER("mysqlnd_vio::network_write");
113 DBG_INF_FMT("sending %u bytes", count);
114 ret = php_stream_write(vio->data->m.get_stream(vio), (char *)buffer, count);
115 DBG_RETURN(ret);
116 }
117 /* }}} */
118
119
120 /* {{{ mysqlnd_vio::open_pipe */
121 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,open_pipe)122 MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
123 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
124 {
125 unsigned int streams_options = 0;
126 dtor_func_t origin_dtor;
127 php_stream * net_stream = NULL;
128
129 DBG_ENTER("mysqlnd_vio::open_pipe");
130 if (persistent) {
131 streams_options |= STREAM_OPEN_PERSISTENT;
132 }
133 streams_options |= IGNORE_URL;
134 net_stream = php_stream_open_wrapper(scheme.s + sizeof("pipe://") - 1, "r+", streams_options, NULL);
135 if (!net_stream) {
136 SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
137 DBG_RETURN(NULL);
138 }
139 /*
140 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
141 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
142 unregistered until the script ends. So, we need to take care of that.
143 */
144 origin_dtor = EG(regular_list).pDestructor;
145 EG(regular_list).pDestructor = NULL;
146 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
147 EG(regular_list).pDestructor = origin_dtor;
148 net_stream->res = NULL;
149
150 DBG_RETURN(net_stream);
151 }
152 /* }}} */
153
154
155 /* {{{ mysqlnd_vio::open_tcp_or_unix */
156 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,open_tcp_or_unix)157 MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
158 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
159 {
160 unsigned int streams_options = 0;
161 unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
162 char * hashed_details = NULL;
163 int hashed_details_len = 0;
164 zend_string *errstr = NULL;
165 int errcode = 0;
166 struct timeval tv;
167 dtor_func_t origin_dtor;
168 php_stream * net_stream = NULL;
169
170 DBG_ENTER("mysqlnd_vio::open_tcp_or_unix");
171
172 vio->data->stream = NULL;
173
174 if (persistent) {
175 hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", vio);
176 DBG_INF_FMT("hashed_details=%s", hashed_details);
177 }
178
179 if (vio->data->options.timeout_connect) {
180 tv.tv_sec = vio->data->options.timeout_connect;
181 tv.tv_usec = 0;
182 }
183
184 DBG_INF_FMT("calling php_stream_xport_create");
185 net_stream = php_stream_xport_create(scheme.s, scheme.l, streams_options, streams_flags,
186 hashed_details, (vio->data->options.timeout_connect) ? &tv : NULL,
187 NULL /*ctx*/, &errstr, &errcode);
188 if (errstr || !net_stream) {
189 DBG_ERR("Error");
190 if (hashed_details) {
191 mnd_sprintf_free(hashed_details);
192 }
193 errcode = CR_CONNECTION_ERROR;
194 SET_CLIENT_ERROR(error_info,
195 CR_CONNECTION_ERROR,
196 UNKNOWN_SQLSTATE,
197 errstr? ZSTR_VAL(errstr):"Unknown error while connecting");
198 if (errstr) {
199 zend_string_release(errstr);
200 }
201 DBG_RETURN(NULL);
202 }
203 if (hashed_details) {
204 /*
205 If persistent, the streams register it in EG(persistent_list).
206 This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
207 whatever they have to.
208 */
209 zend_resource *le;
210
211 if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
212 origin_dtor = EG(persistent_list).pDestructor;
213 /*
214 in_free will let streams code skip destructing - big HACK,
215 but STREAMS suck big time regarding persistent streams.
216 Just not compatible for extensions that need persistency.
217 */
218 EG(persistent_list).pDestructor = NULL;
219 zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
220 EG(persistent_list).pDestructor = origin_dtor;
221 pefree(le, 1);
222 }
223 #if ZEND_DEBUG
224 /* Shut-up the streams, they don't know what they are doing */
225 net_stream->__exposed = 1;
226 #endif
227 mnd_sprintf_free(hashed_details);
228 }
229
230 /*
231 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
232 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
233 unregistered until the script ends. So, we need to take care of that.
234 */
235 origin_dtor = EG(regular_list).pDestructor;
236 EG(regular_list).pDestructor = NULL;
237 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
238 efree(net_stream->res);
239 net_stream->res = NULL;
240 EG(regular_list).pDestructor = origin_dtor;
241 DBG_RETURN(net_stream);
242 }
243 /* }}} */
244
245
246 /* {{{ mysqlnd_vio::post_connect_set_opt */
247 static void
MYSQLND_METHOD(mysqlnd_vio,post_connect_set_opt)248 MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
249 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
250 {
251 php_stream * net_stream = vio->data->m.get_stream(vio);
252 DBG_ENTER("mysqlnd_vio::post_connect_set_opt");
253 if (net_stream) {
254 if (vio->data->options.timeout_read) {
255 struct timeval tv;
256 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", vio->data->options.timeout_read);
257 tv.tv_sec = vio->data->options.timeout_read;
258 tv.tv_usec = 0;
259 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
260 }
261
262 if (!memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1)) {
263 /* TCP -> Set TCP_NODELAY */
264 mysqlnd_set_sock_no_delay(net_stream);
265 /* TCP -> Set SO_KEEPALIVE */
266 mysqlnd_set_sock_keepalive(net_stream);
267 }
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 int bytes_consumed;
456
457 do {
458 skipped_bytes += (bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf)));
459 } while (bytes_consumed == sizeof(tmp_buf));
460
461 if (was_blocked) {
462 net_stream->ops->set_option(net_stream, opt, 1, NULL);
463 }
464
465 if (bytes_consumed) {
466 DBG_ERR_FMT("Skipped %u bytes. Last command hasn't consumed all the output from the server",
467 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
468 php_error_docref(NULL, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
469 "consumed all the output from the server",
470 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
471 }
472 }
473 net->last_command = cmd;
474
475 DBG_RETURN(skipped_bytes);
476 #else
477 return 0;
478 #endif
479 }
480 /* }}} */
481
482 /*
483 in libmyusql, if cert and !key then key=cert
484 */
485 /* {{{ mysqlnd_vio::enable_ssl */
486 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,enable_ssl)487 MYSQLND_METHOD(mysqlnd_vio, enable_ssl)(MYSQLND_VIO * const net)
488 {
489 #ifdef MYSQLND_SSL_SUPPORTED
490 php_stream_context * context = php_stream_context_alloc();
491 php_stream * net_stream = net->data->m.get_stream(net);
492 zend_bool any_flag = FALSE;
493
494 DBG_ENTER("mysqlnd_vio::enable_ssl");
495 if (!context) {
496 DBG_RETURN(FAIL);
497 }
498
499 if (net->data->options.ssl_key) {
500 zval key_zval;
501 ZVAL_STRING(&key_zval, net->data->options.ssl_key);
502 php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
503 zval_ptr_dtor(&key_zval);
504 any_flag = TRUE;
505 }
506 if (net->data->options.ssl_cert) {
507 zval cert_zval;
508 ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
509 php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
510 if (!net->data->options.ssl_key) {
511 php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
512 }
513 zval_ptr_dtor(&cert_zval);
514 any_flag = TRUE;
515 }
516 if (net->data->options.ssl_ca) {
517 zval cafile_zval;
518 ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
519 php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
520 zval_ptr_dtor(&cafile_zval);
521 any_flag = TRUE;
522 }
523 if (net->data->options.ssl_capath) {
524 zval capath_zval;
525 ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
526 php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
527 zval_ptr_dtor(&capath_zval);
528 any_flag = TRUE;
529 }
530 if (net->data->options.ssl_passphrase) {
531 zval passphrase_zval;
532 ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
533 php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
534 zval_ptr_dtor(&passphrase_zval);
535 any_flag = TRUE;
536 }
537 if (net->data->options.ssl_cipher) {
538 zval cipher_zval;
539 ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
540 php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
541 zval_ptr_dtor(&cipher_zval);
542 any_flag = TRUE;
543 }
544 {
545 zval verify_peer_zval;
546 zend_bool verify;
547
548 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
549 net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
550 }
551
552 verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
553
554 DBG_INF_FMT("VERIFY=%d", verify);
555 ZVAL_BOOL(&verify_peer_zval, verify);
556 php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
557 php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
558 if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
559 ZVAL_TRUE(&verify_peer_zval);
560 php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
561 }
562 }
563 php_stream_context_set(net_stream, context);
564 if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
565 php_stream_xport_crypto_enable(net_stream, 1) < 0)
566 {
567 DBG_ERR("Cannot connect to MySQL by using SSL");
568 php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
569 DBG_RETURN(FAIL);
570 }
571 net->data->ssl = TRUE;
572 /*
573 get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
574 then the context would not survive cleaning of EG(regular_list), where it is registered, as a
575 resource. What happens is that after this destruction any use of the network will mean usage
576 of the context, which means usage of already freed memory, bad. Actually we don't need this
577 context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
578 */
579 php_stream_context_set(net_stream, NULL);
580
581 if (net->data->options.timeout_read) {
582 struct timeval tv;
583 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
584 tv.tv_sec = net->data->options.timeout_read;
585 tv.tv_usec = 0;
586 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
587 }
588
589 DBG_RETURN(PASS);
590 #else
591 DBG_ENTER("mysqlnd_vio::enable_ssl");
592 DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
593 DBG_RETURN(PASS);
594 #endif
595 }
596 /* }}} */
597
598
599 /* {{{ mysqlnd_vio::disable_ssl */
600 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,disable_ssl)601 MYSQLND_METHOD(mysqlnd_vio, disable_ssl)(MYSQLND_VIO * const vio)
602 {
603 DBG_ENTER("mysqlnd_vio::disable_ssl");
604 DBG_RETURN(PASS);
605 }
606 /* }}} */
607
608
609 /* {{{ mysqlnd_vio::free_contents */
610 static void
MYSQLND_METHOD(mysqlnd_vio,free_contents)611 MYSQLND_METHOD(mysqlnd_vio, free_contents)(MYSQLND_VIO * net)
612 {
613 zend_bool pers = net->persistent;
614 DBG_ENTER("mysqlnd_vio::free_contents");
615
616 if (net->data->options.ssl_key) {
617 mnd_pefree(net->data->options.ssl_key, pers);
618 net->data->options.ssl_key = NULL;
619 }
620 if (net->data->options.ssl_cert) {
621 mnd_pefree(net->data->options.ssl_cert, pers);
622 net->data->options.ssl_cert = NULL;
623 }
624 if (net->data->options.ssl_ca) {
625 mnd_pefree(net->data->options.ssl_ca, pers);
626 net->data->options.ssl_ca = NULL;
627 }
628 if (net->data->options.ssl_capath) {
629 mnd_pefree(net->data->options.ssl_capath, pers);
630 net->data->options.ssl_capath = NULL;
631 }
632 if (net->data->options.ssl_cipher) {
633 mnd_pefree(net->data->options.ssl_cipher, pers);
634 net->data->options.ssl_cipher = NULL;
635 }
636
637 DBG_VOID_RETURN;
638 }
639 /* }}} */
640
641
642 /* {{{ mysqlnd_vio::close_stream */
643 static void
MYSQLND_METHOD(mysqlnd_vio,close_stream)644 MYSQLND_METHOD(mysqlnd_vio, close_stream)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
645 {
646 php_stream * net_stream;
647 DBG_ENTER("mysqlnd_vio::close_stream");
648 if (net && (net_stream = net->data->m.get_stream(net))) {
649 zend_bool pers = net->persistent;
650 DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
651 if (pers) {
652 if (EG(active)) {
653 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
654 } else {
655 /*
656 otherwise we will crash because the EG(persistent_list) has been freed already,
657 before the modules are shut down
658 */
659 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
660 }
661 } else {
662 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
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->data, vio->data->persistent);
700 mnd_pefree(vio, vio->persistent);
701 }
702 DBG_VOID_RETURN;
703 }
704 /* }}} */
705
706
707 /* {{{ mysqlnd_vio::get_stream */
708 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,get_stream)709 MYSQLND_METHOD(mysqlnd_vio, get_stream)(const MYSQLND_VIO * const net)
710 {
711 DBG_ENTER("mysqlnd_vio::get_stream");
712 DBG_INF_FMT("%p", net? net->data->stream:NULL);
713 DBG_RETURN(net? net->data->stream:NULL);
714 }
715 /* }}} */
716
717
718 /* {{{ mysqlnd_vio::set_stream */
719 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,set_stream)720 MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const vio, php_stream * net_stream)
721 {
722 DBG_ENTER("mysqlnd_vio::set_stream");
723 if (vio) {
724 vio->data->stream = net_stream;
725 DBG_RETURN(PASS);
726 }
727 DBG_RETURN(FAIL);
728 }
729 /* }}} */
730
731
732 /* {{{ mysqlnd_vio::has_valid_stream */
733 static zend_bool
MYSQLND_METHOD(mysqlnd_vio,has_valid_stream)734 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream)(const MYSQLND_VIO * const vio)
735 {
736 DBG_ENTER("mysqlnd_vio::has_valid_stream");
737 DBG_INF_FMT("%p %p", vio, vio? vio->data->stream:NULL);
738 DBG_RETURN((vio && vio->data->stream)? TRUE: FALSE);
739 }
740 /* }}} */
741
742
743 MYSQLND_CLASS_METHODS_START(mysqlnd_vio)
744 MYSQLND_METHOD(mysqlnd_vio, init),
745 MYSQLND_METHOD(mysqlnd_vio, dtor),
746
747 MYSQLND_METHOD(mysqlnd_vio, connect),
748
749 MYSQLND_METHOD(mysqlnd_vio, close_stream),
750 MYSQLND_METHOD(mysqlnd_vio, open_pipe),
751 MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix),
752
753 MYSQLND_METHOD(mysqlnd_vio, get_stream),
754 MYSQLND_METHOD(mysqlnd_vio, set_stream),
755 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream),
756 MYSQLND_METHOD(mysqlnd_vio, get_open_stream),
757
758 MYSQLND_METHOD(mysqlnd_vio, set_client_option),
759 MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt),
760
761 MYSQLND_METHOD(mysqlnd_vio, enable_ssl),
762 MYSQLND_METHOD(mysqlnd_vio, disable_ssl),
763
764 MYSQLND_METHOD(mysqlnd_vio, network_read),
765 MYSQLND_METHOD(mysqlnd_vio, network_write),
766
767 MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data),
768
769 MYSQLND_METHOD(mysqlnd_vio, free_contents),
770 MYSQLND_CLASS_METHODS_END;
771
772
773 /* {{{ mysqlnd_vio_init */
774 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)775 mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
776 {
777 MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
778 MYSQLND_VIO * vio;
779 DBG_ENTER("mysqlnd_vio_init");
780 vio = factory->get_vio(persistent, stats, error_info);
781 DBG_RETURN(vio);
782 }
783 /* }}} */
784
785
786 /* {{{ mysqlnd_vio_free */
787 PHPAPI void
mysqlnd_vio_free(MYSQLND_VIO * const vio,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)788 mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
789 {
790 DBG_ENTER("mysqlnd_vio_free");
791 if (vio) {
792 vio->data->m.dtor(vio, stats, error_info);
793 }
794 DBG_VOID_RETURN;
795 }
796 /* }}} */
797
798
799 /*
800 * Local variables:
801 * tab-width: 4
802 * c-basic-offset: 4
803 * End:
804 * vim600: noet sw=4 ts=4 fdm=marker
805 * vim<600: noet sw=4 ts=4
806 */
807