xref: /PHP-7.2/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 
496 	if (net->data->options.ssl_key) {
497 		zval key_zval;
498 		ZVAL_STRING(&key_zval, net->data->options.ssl_key);
499 		php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
500 		zval_ptr_dtor(&key_zval);
501 		any_flag = TRUE;
502 	}
503 	if (net->data->options.ssl_cert) {
504 		zval cert_zval;
505 		ZVAL_STRING(&cert_zval, net->data->options.ssl_cert);
506 		php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
507 		if (!net->data->options.ssl_key) {
508 			php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
509 		}
510 		zval_ptr_dtor(&cert_zval);
511 		any_flag = TRUE;
512 	}
513 	if (net->data->options.ssl_ca) {
514 		zval cafile_zval;
515 		ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca);
516 		php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
517 		zval_ptr_dtor(&cafile_zval);
518 		any_flag = TRUE;
519 	}
520 	if (net->data->options.ssl_capath) {
521 		zval capath_zval;
522 		ZVAL_STRING(&capath_zval, net->data->options.ssl_capath);
523 		php_stream_context_set_option(context, "ssl", "capath", &capath_zval);
524 		zval_ptr_dtor(&capath_zval);
525 		any_flag = TRUE;
526 	}
527 	if (net->data->options.ssl_passphrase) {
528 		zval passphrase_zval;
529 		ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase);
530 		php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
531 		zval_ptr_dtor(&passphrase_zval);
532 		any_flag = TRUE;
533 	}
534 	if (net->data->options.ssl_cipher) {
535 		zval cipher_zval;
536 		ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher);
537 		php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
538 		zval_ptr_dtor(&cipher_zval);
539 		any_flag = TRUE;
540 	}
541 	{
542 		zval verify_peer_zval;
543 		zend_bool verify;
544 
545 		if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DEFAULT) {
546 			net->data->options.ssl_verify_peer = any_flag? MYSQLND_SSL_PEER_DEFAULT_ACTION:MYSQLND_SSL_PEER_DONT_VERIFY;
547 		}
548 
549 		verify = net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_VERIFY? TRUE:FALSE;
550 
551 		DBG_INF_FMT("VERIFY=%d", verify);
552 		ZVAL_BOOL(&verify_peer_zval, verify);
553 		php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
554 		php_stream_context_set_option(context, "ssl", "verify_peer_name", &verify_peer_zval);
555 		if (net->data->options.ssl_verify_peer == MYSQLND_SSL_PEER_DONT_VERIFY) {
556 			ZVAL_TRUE(&verify_peer_zval);
557 			php_stream_context_set_option(context, "ssl", "allow_self_signed", &verify_peer_zval);
558 		}
559 	}
560 	php_stream_context_set(net_stream, context);
561 	if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL) < 0 ||
562 	    php_stream_xport_crypto_enable(net_stream, 1) < 0)
563 	{
564 		DBG_ERR("Cannot connect to MySQL by using SSL");
565 		php_error_docref(NULL, E_WARNING, "Cannot connect to MySQL by using SSL");
566 		DBG_RETURN(FAIL);
567 	}
568 	net->data->ssl = TRUE;
569 	/*
570 	  get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
571 	  then the context would not survive cleaning of EG(regular_list), where it is registered, as a
572 	  resource. What happens is that after this destruction any use of the network will mean usage
573 	  of the context, which means usage of already freed memory, bad. Actually we don't need this
574 	  context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
575 	*/
576 	php_stream_context_set(net_stream, NULL);
577 
578 	if (net->data->options.timeout_read) {
579 		struct timeval tv;
580 		DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
581 		tv.tv_sec = net->data->options.timeout_read;
582 		tv.tv_usec = 0;
583 		php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
584 	}
585 
586 	DBG_RETURN(PASS);
587 #else
588 	DBG_ENTER("mysqlnd_vio::enable_ssl");
589 	DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
590 	DBG_RETURN(PASS);
591 #endif
592 }
593 /* }}} */
594 
595 
596 /* {{{ mysqlnd_vio::disable_ssl */
597 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,disable_ssl)598 MYSQLND_METHOD(mysqlnd_vio, disable_ssl)(MYSQLND_VIO * const vio)
599 {
600 	DBG_ENTER("mysqlnd_vio::disable_ssl");
601 	DBG_RETURN(PASS);
602 }
603 /* }}} */
604 
605 
606 /* {{{ mysqlnd_vio::free_contents */
607 static void
MYSQLND_METHOD(mysqlnd_vio,free_contents)608 MYSQLND_METHOD(mysqlnd_vio, free_contents)(MYSQLND_VIO * net)
609 {
610 	zend_bool pers = net->persistent;
611 	DBG_ENTER("mysqlnd_vio::free_contents");
612 
613 	if (net->data->options.ssl_key) {
614 		mnd_pefree(net->data->options.ssl_key, pers);
615 		net->data->options.ssl_key = NULL;
616 	}
617 	if (net->data->options.ssl_cert) {
618 		mnd_pefree(net->data->options.ssl_cert, pers);
619 		net->data->options.ssl_cert = NULL;
620 	}
621 	if (net->data->options.ssl_ca) {
622 		mnd_pefree(net->data->options.ssl_ca, pers);
623 		net->data->options.ssl_ca = NULL;
624 	}
625 	if (net->data->options.ssl_capath) {
626 		mnd_pefree(net->data->options.ssl_capath, pers);
627 		net->data->options.ssl_capath = NULL;
628 	}
629 	if (net->data->options.ssl_cipher) {
630 		mnd_pefree(net->data->options.ssl_cipher, pers);
631 		net->data->options.ssl_cipher = NULL;
632 	}
633 
634 	DBG_VOID_RETURN;
635 }
636 /* }}} */
637 
638 
639 /* {{{ mysqlnd_vio::close_stream */
640 static void
MYSQLND_METHOD(mysqlnd_vio,close_stream)641 MYSQLND_METHOD(mysqlnd_vio, close_stream)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
642 {
643 	php_stream * net_stream;
644 	DBG_ENTER("mysqlnd_vio::close_stream");
645 	if (net && (net_stream = net->data->m.get_stream(net))) {
646 		zend_bool pers = net->persistent;
647 		DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
648 		if (pers) {
649 			if (EG(active)) {
650 				php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
651 			} else {
652 				/*
653 				  otherwise we will crash because the EG(persistent_list) has been freed already,
654 				  before the modules are shut down
655 				*/
656 				php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
657 			}
658 		} else {
659 			php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
660 		}
661 		net->data->m.set_stream(net, NULL);
662 	}
663 
664 	DBG_VOID_RETURN;
665 }
666 /* }}} */
667 
668 
669 /* {{{ mysqlnd_vio::init */
670 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,init)671 MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
672 {
673 	unsigned int buf_size;
674 	DBG_ENTER("mysqlnd_vio::init");
675 
676 	buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
677 	net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size);
678 
679 	buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
680 	net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size);
681 
682 	DBG_RETURN(PASS);
683 }
684 /* }}} */
685 
686 
687 /* {{{ mysqlnd_vio::dtor */
688 static void
MYSQLND_METHOD(mysqlnd_vio,dtor)689 MYSQLND_METHOD(mysqlnd_vio, dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
690 {
691 	DBG_ENTER("mysqlnd_vio::dtor");
692 	if (vio) {
693 		vio->data->m.free_contents(vio);
694 		vio->data->m.close_stream(vio, stats, error_info);
695 
696 		mnd_pefree(vio->data, vio->data->persistent);
697 		mnd_pefree(vio, vio->persistent);
698 	}
699 	DBG_VOID_RETURN;
700 }
701 /* }}} */
702 
703 
704 /* {{{ mysqlnd_vio::get_stream */
705 static php_stream *
MYSQLND_METHOD(mysqlnd_vio,get_stream)706 MYSQLND_METHOD(mysqlnd_vio, get_stream)(const MYSQLND_VIO * const net)
707 {
708 	DBG_ENTER("mysqlnd_vio::get_stream");
709 	DBG_INF_FMT("%p", net? net->data->stream:NULL);
710 	DBG_RETURN(net? net->data->stream:NULL);
711 }
712 /* }}} */
713 
714 
715 /* {{{ mysqlnd_vio::set_stream */
716 static enum_func_status
MYSQLND_METHOD(mysqlnd_vio,set_stream)717 MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const vio, php_stream * net_stream)
718 {
719 	DBG_ENTER("mysqlnd_vio::set_stream");
720 	if (vio) {
721 		vio->data->stream = net_stream;
722 		DBG_RETURN(PASS);
723 	}
724 	DBG_RETURN(FAIL);
725 }
726 /* }}} */
727 
728 
729 /* {{{ mysqlnd_vio::has_valid_stream */
730 static zend_bool
MYSQLND_METHOD(mysqlnd_vio,has_valid_stream)731 MYSQLND_METHOD(mysqlnd_vio, has_valid_stream)(const MYSQLND_VIO * const vio)
732 {
733 	DBG_ENTER("mysqlnd_vio::has_valid_stream");
734 	DBG_INF_FMT("%p %p", vio, vio? vio->data->stream:NULL);
735 	DBG_RETURN((vio && vio->data->stream)? TRUE: FALSE);
736 }
737 /* }}} */
738 
739 
740 MYSQLND_CLASS_METHODS_START(mysqlnd_vio)
741 	MYSQLND_METHOD(mysqlnd_vio, init),
742 	MYSQLND_METHOD(mysqlnd_vio, dtor),
743 
744 	MYSQLND_METHOD(mysqlnd_vio, connect),
745 
746 	MYSQLND_METHOD(mysqlnd_vio, close_stream),
747 	MYSQLND_METHOD(mysqlnd_vio, open_pipe),
748 	MYSQLND_METHOD(mysqlnd_vio, open_tcp_or_unix),
749 
750 	MYSQLND_METHOD(mysqlnd_vio, get_stream),
751 	MYSQLND_METHOD(mysqlnd_vio, set_stream),
752 	MYSQLND_METHOD(mysqlnd_vio, has_valid_stream),
753 	MYSQLND_METHOD(mysqlnd_vio, get_open_stream),
754 
755 	MYSQLND_METHOD(mysqlnd_vio, set_client_option),
756 	MYSQLND_METHOD(mysqlnd_vio, post_connect_set_opt),
757 
758 	MYSQLND_METHOD(mysqlnd_vio, enable_ssl),
759 	MYSQLND_METHOD(mysqlnd_vio, disable_ssl),
760 
761 	MYSQLND_METHOD(mysqlnd_vio, network_read),
762 	MYSQLND_METHOD(mysqlnd_vio, network_write),
763 
764 	MYSQLND_METHOD(mysqlnd_vio, consume_uneaten_data),
765 
766 	MYSQLND_METHOD(mysqlnd_vio, free_contents),
767 MYSQLND_CLASS_METHODS_END;
768 
769 
770 /* {{{ mysqlnd_vio_init */
771 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)772 mysqlnd_vio_init(zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
773 {
774 	MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
775 	MYSQLND_VIO * vio;
776 	DBG_ENTER("mysqlnd_vio_init");
777 	vio = factory->get_vio(persistent, stats, error_info);
778 	DBG_RETURN(vio);
779 }
780 /* }}} */
781 
782 
783 /* {{{ mysqlnd_vio_free */
784 PHPAPI void
mysqlnd_vio_free(MYSQLND_VIO * const vio,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info)785 mysqlnd_vio_free(MYSQLND_VIO * const vio, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info)
786 {
787 	DBG_ENTER("mysqlnd_vio_free");
788 	if (vio) {
789 		vio->data->m.dtor(vio, stats, error_info);
790 	}
791 	DBG_VOID_RETURN;
792 }
793 /* }}} */
794 
795 
796 /*
797  * Local variables:
798  * tab-width: 4
799  * c-basic-offset: 4
800  * End:
801  * vim600: noet sw=4 ts=4 fdm=marker
802  * vim<600: noet sw=4 ts=4
803  */
804