xref: /PHP-7.3/ext/mysqlnd/mysqlnd_vio.c (revision fcfa006a)
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