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