1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Andrey Hristov <andrey@php.net> |
14 | Ulf Wendel <uw@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #include "php.h"
19 #include "mysqlnd.h"
20 #include "mysqlnd_priv.h"
21 #include "mysqlnd_statistics.h"
22 #include "mysqlnd_debug.h"
23 #include "mysqlnd_ext_plugin.h"
24 #include "php_network.h"
25
26 #ifndef PHP_WIN32
27 #include <netinet/tcp.h>
28 #else
29 #include <winsock.h>
30 #endif
31
32
33 /* {{{ mysqlnd_set_sock_no_delay */
34 static int
mysqlnd_set_sock_no_delay(php_stream * stream)35 mysqlnd_set_sock_no_delay(php_stream * stream)
36 {
37 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
38 int ret = SUCCESS;
39 int flag = 1;
40 int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
41
42 DBG_ENTER("mysqlnd_set_sock_no_delay");
43
44 if (result == -1) {
45 ret = FAILURE;
46 }
47
48 DBG_RETURN(ret);
49 }
50 /* }}} */
51
52
53 /* {{{ mysqlnd_set_sock_keepalive */
54 static int
mysqlnd_set_sock_keepalive(php_stream * stream)55 mysqlnd_set_sock_keepalive(php_stream * stream)
56 {
57 int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
58 int ret = SUCCESS;
59 int flag = 1;
60 int result = setsockopt(socketd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int));
61
62 DBG_ENTER("mysqlnd_set_sock_keepalive");
63
64 if (result == -1) {
65 ret = FAILURE;
66 }
67
68 DBG_RETURN(ret);
69 }
70 /* }}} */
71
72
73 /* {{{ mysqlnd_vio::network_read */
74 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,network_read)75 MYSQLND_METHOD(mysqlnd_vio, network_read)(MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count,
76 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
77 {
78 enum_func_status return_value = PASS;
79 php_stream * net_stream = vio->data->m.get_stream(vio);
80 size_t to_read = count;
81 zend_uchar * p = buffer;
82
83 DBG_ENTER("mysqlnd_vio::network_read");
84 DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
85
86 while (to_read) {
87 ssize_t ret = php_stream_read(net_stream, (char *) p, to_read);
88 if (ret <= 0) {
89 DBG_ERR_FMT("Error while reading header from socket");
90 return_value = FAIL;
91 break;
92 }
93 p += ret;
94 to_read -= ret;
95 }
96 MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
97 DBG_RETURN(return_value);
98 }
99 /* }}} */
100
101
102 /* {{{ mysqlnd_vio::network_write */
103 static ssize_t
MYSQLND_METHOD(mysqlnd_vio,network_write)104 MYSQLND_METHOD(mysqlnd_vio, network_write)(MYSQLND_VIO * const vio, const zend_uchar * const buffer, const size_t count,
105 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
106 {
107 ssize_t ret;
108 DBG_ENTER("mysqlnd_vio::network_write");
109 DBG_INF_FMT("sending %u bytes", count);
110 ret = php_stream_write(vio->data->m.get_stream(vio), (char *)buffer, count);
111 DBG_RETURN(ret);
112 }
113 /* }}} */
114
115
116 /* {{{ mysqlnd_vio::open_pipe */
117 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,open_pipe)118 MYSQLND_METHOD(mysqlnd_vio, open_pipe)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
119 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
120 {
121 unsigned int streams_options = 0;
122 dtor_func_t origin_dtor;
123 php_stream * net_stream = NULL;
124
125 DBG_ENTER("mysqlnd_vio::open_pipe");
126 if (persistent) {
127 streams_options |= STREAM_OPEN_PERSISTENT;
128 }
129 streams_options |= IGNORE_URL;
130 net_stream = php_stream_open_wrapper(scheme.s + sizeof("pipe://") - 1, "r+", streams_options, NULL);
131 if (!net_stream) {
132 SET_CLIENT_ERROR(error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown error while connecting");
133 DBG_RETURN(NULL);
134 }
135 /*
136 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
137 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
138 unregistered until the script ends. So, we need to take care of that.
139 */
140 origin_dtor = EG(regular_list).pDestructor;
141 EG(regular_list).pDestructor = NULL;
142 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
143 EG(regular_list).pDestructor = origin_dtor;
144 net_stream->res = NULL;
145
146 DBG_RETURN(net_stream);
147 }
148 /* }}} */
149
150
151 /* {{{ mysqlnd_vio::open_tcp_or_unix */
152 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,open_tcp_or_unix)153 MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent,
154 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
155 {
156 unsigned int streams_options = 0;
157 unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
158 char * hashed_details = NULL;
159 int hashed_details_len = 0;
160 zend_string *errstr = NULL;
161 int errcode = 0;
162 struct timeval tv;
163 dtor_func_t origin_dtor;
164 php_stream * net_stream = NULL;
165
166 DBG_ENTER("mysqlnd_vio::open_tcp_or_unix");
167
168 vio->data->stream = NULL;
169
170 if (persistent) {
171 hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", vio);
172 DBG_INF_FMT("hashed_details=%s", hashed_details);
173 }
174
175 if (vio->data->options.timeout_connect) {
176 tv.tv_sec = vio->data->options.timeout_connect;
177 tv.tv_usec = 0;
178 }
179
180 DBG_INF_FMT("calling php_stream_xport_create");
181 net_stream = php_stream_xport_create(scheme.s, scheme.l, streams_options, streams_flags,
182 hashed_details, (vio->data->options.timeout_connect) ? &tv : NULL,
183 NULL /*ctx*/, &errstr, &errcode);
184 if (errstr || !net_stream) {
185 DBG_ERR("Error");
186 if (hashed_details) {
187 mnd_sprintf_free(hashed_details);
188 }
189 errcode = CR_CONNECTION_ERROR;
190 SET_CLIENT_ERROR(error_info,
191 CR_CONNECTION_ERROR,
192 UNKNOWN_SQLSTATE,
193 errstr? ZSTR_VAL(errstr):"Unknown error while connecting");
194 if (errstr) {
195 zend_string_release_ex(errstr, 0);
196 }
197 DBG_RETURN(NULL);
198 }
199 if (hashed_details) {
200 /*
201 If persistent, the streams register it in EG(persistent_list).
202 This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
203 whatever they have to.
204 */
205 zend_resource *le;
206
207 if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len))) {
208 origin_dtor = EG(persistent_list).pDestructor;
209 /*
210 in_free will let streams code skip destructing - big HACK,
211 but STREAMS suck big time regarding persistent streams.
212 Just not compatible for extensions that need persistency.
213 */
214 EG(persistent_list).pDestructor = NULL;
215 zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len);
216 EG(persistent_list).pDestructor = origin_dtor;
217 pefree(le, 1);
218 }
219 #if ZEND_DEBUG
220 /* Shut-up the streams, they don't know what they are doing */
221 net_stream->__exposed = 1;
222 #endif
223 mnd_sprintf_free(hashed_details);
224 }
225
226 /*
227 Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
228 be registered as resource (in EG(regular_list). So far, so good. However, it won't be
229 unregistered until the script ends. So, we need to take care of that.
230 */
231 origin_dtor = EG(regular_list).pDestructor;
232 EG(regular_list).pDestructor = NULL;
233 zend_hash_index_del(&EG(regular_list), net_stream->res->handle); /* ToDO: should it be res->handle, do streams register with addref ?*/
234 efree(net_stream->res);
235 net_stream->res = NULL;
236 EG(regular_list).pDestructor = origin_dtor;
237 DBG_RETURN(net_stream);
238 }
239 /* }}} */
240
241
242 /* {{{ mysqlnd_vio::post_connect_set_opt */
243 static void
MYSQLND_METHOD(mysqlnd_vio,post_connect_set_opt)244 MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme,
245 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info)
246 {
247 php_stream * net_stream = vio->data->m.get_stream(vio);
248 DBG_ENTER("mysqlnd_vio::post_connect_set_opt");
249 if (net_stream) {
250 if (vio->data->options.timeout_read) {
251 struct timeval tv;
252 DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", vio->data->options.timeout_read);
253 tv.tv_sec = vio->data->options.timeout_read;
254 tv.tv_usec = 0;
255 php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
256 }
257
258 if (!memcmp(scheme.s, "tcp://", sizeof("tcp://") - 1)) {
259 /* TCP -> Set TCP_NODELAY */
260 mysqlnd_set_sock_no_delay(net_stream);
261 /* TCP -> Set SO_KEEPALIVE */
262 mysqlnd_set_sock_keepalive(net_stream);
263 }
264
265 net_stream->chunk_size = vio->data->options.net_read_buffer_size;
266 net_stream->flags |= PHP_STREAM_FLAG_SUPPRESS_ERRORS;
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 ssize_t bytes_consumed;
455
456 do {
457 bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf));
458 if (bytes_consumed <= 0) {
459 break;
460 }
461 skipped_bytes += bytes_consumed;
462 } while (bytes_consumed == sizeof(tmp_buf));
463
464 if (was_blocked) {
465 net_stream->ops->set_option(net_stream, opt, 1, NULL);
466 }
467
468 if (bytes_consumed) {
469 DBG_ERR_FMT("Skipped %u bytes. Last command hasn't consumed all the output from the server",
470 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
471 php_error_docref(NULL, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
472 "consumed all the output from the server",
473 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
474 }
475 }
476 net->last_command = cmd;
477
478 DBG_RETURN(skipped_bytes);
479 #else
480 return 0;
481 #endif
482 }
483 /* }}} */
484
485 /*
486 in libmyusql, if cert and !key then key=cert
487 */
488 /* {{{ mysqlnd_vio::enable_ssl */
489 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,enable_ssl)490 MYSQLND_METHOD(mysqlnd_vio, enable_ssl)(MYSQLND_VIO * const net)
491 {
492 #ifdef MYSQLND_SSL_SUPPORTED
493 php_stream_context * context = php_stream_context_alloc();
494 php_stream * net_stream = net->data->m.get_stream(net);
495 zend_bool any_flag = FALSE;
496
497 DBG_ENTER("mysqlnd_vio::enable_ssl");
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 /* We removed the resource from the stream, so pass FREE_RSRC_DTOR now to force
652 * destruction to occur during shutdown, because it won't happen through the resource. */
653 /* TODO: The EG(active) check here is dead -- check IN_SHUTDOWN? */
654 if (pers && EG(active)) {
655 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
656 } else {
657 /*
658 otherwise we will crash because the EG(persistent_list) has been freed already,
659 before the modules are shut down
660 */
661 php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
662 }
663 net->data->m.set_stream(net, NULL);
664 }
665
666 DBG_VOID_RETURN;
667 }
668 /* }}} */
669
670
671 /* {{{ mysqlnd_vio::init */
672 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,init)673 MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
674 {
675 unsigned int buf_size;
676 DBG_ENTER("mysqlnd_vio::init");
677
678 buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
679 net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
680
681 buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
682 net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
683
684 DBG_RETURN(PASS);
685 }
686 /* }}} */
687
688
689 /* {{{ mysqlnd_vio::dtor */
690 static void
MYSQLND_METHOD(mysqlnd_vio,dtor)691 MYSQLND_METHOD(mysqlnd_vio, dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
692 {
693 DBG_ENTER("mysqlnd_vio::dtor");
694 if (vio) {
695 vio->data->m.free_contents(vio);
696 vio->data->m.close_stream(vio, stats, error_info);
697
698 mnd_pefree(vio, vio->persistent);
699 }
700 DBG_VOID_RETURN;
701 }
702 /* }}} */
703
704
705 /* {{{ mysqlnd_vio::get_stream */
706 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,get_stream)707 MYSQLND_METHOD(mysqlnd_vio, get_stream)(const MYSQLND_VIO * const net)
708 {
709 DBG_ENTER("mysqlnd_vio::get_stream");
710 DBG_INF_FMT("%p", net? net->data->stream:NULL);
711 DBG_RETURN(net? net->data->stream:NULL);
712 }
713 /* }}} */
714
715
716 /* {{{ mysqlnd_vio::set_stream */
717 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,set_stream)718 MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const vio, php_stream * net_stream)
719 {
720 DBG_ENTER("mysqlnd_vio::set_stream");
721 if (vio) {
722 vio->data->stream = net_stream;
723 DBG_RETURN(PASS);
724 }
725 DBG_RETURN(FAIL);
726 }
727 /* }}} */
728
729
730 /* {{{ mysqlnd_vio::has_valid_stream */
731 static zend_bool
MYSQLND_METHOD(mysqlnd_vio,has_valid_stream)732 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream)(const MYSQLND_VIO * const vio)
733 {
734 DBG_ENTER("mysqlnd_vio::has_valid_stream");
735 DBG_INF_FMT("%p %p", vio, vio? vio->data->stream:NULL);
736 DBG_RETURN((vio && vio->data->stream)? TRUE: FALSE);
737 }
738 /* }}} */
739
740
741 MYSQLND_CLASS_METHODS_START(mysqlnd_vio)
742 MYSQLND_METHOD(mysqlnd_vio, init),
743 MYSQLND_METHOD(mysqlnd_vio, dtor),
744
745 MYSQLND_METHOD(mysqlnd_vio, connect),
746
747 MYSQLND_METHOD(mysqlnd_vio, close_stream),
748 MYSQLND_METHOD(mysqlnd_vio, open_pipe),
749 MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix),
750
751 MYSQLND_METHOD(mysqlnd_vio, get_stream),
752 MYSQLND_METHOD(mysqlnd_vio, set_stream),
753 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream),
754 MYSQLND_METHOD(mysqlnd_vio, get_open_stream),
755
756 MYSQLND_METHOD(mysqlnd_vio, set_client_option),
757 MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt),
758
759 MYSQLND_METHOD(mysqlnd_vio, enable_ssl),
760 MYSQLND_METHOD(mysqlnd_vio, disable_ssl),
761
762 MYSQLND_METHOD(mysqlnd_vio, network_read),
763 MYSQLND_METHOD(mysqlnd_vio, network_write),
764
765 MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data),
766
767 MYSQLND_METHOD(mysqlnd_vio, free_contents),
768 MYSQLND_CLASS_METHODS_END;
769
770
771 /* {{{ mysqlnd_vio_init */
772 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)773 mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
774 {
775 MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
776 MYSQLND_VIO * vio;
777 DBG_ENTER("mysqlnd_vio_init");
778 vio = factory->get_vio(persistent, stats, error_info);
779 DBG_RETURN(vio);
780 }
781 /* }}} */
782
783
784 /* {{{ mysqlnd_vio_free */
785 PHPAPI void
mysqlnd_vio_free(MYSQLND_VIO * const vio,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)786 mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
787 {
788 DBG_ENTER("mysqlnd_vio_free");
789 if (vio) {
790 vio->data->m.dtor(vio, stats, error_info);
791 }
792 DBG_VOID_RETURN;
793 }
794 /* }}} */
795