xref: /PHP-5.5/ext/mysqlnd/mysqlnd_net.c (revision 73c1be26)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2015 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@mysql.com>                           |
16   |          Ulf Wendel <uwendel@mysql.com>                              |
17   |          Georg Richter <georg@mysql.com>                             |
18   +----------------------------------------------------------------------+
19 */
20 
21 /* $Id: mysqlnd_ps.c 316906 2011-09-17 10:24:18Z pajoye $ */
22 #include "php.h"
23 #include "php_globals.h"
24 #include "mysqlnd.h"
25 #include "mysqlnd_priv.h"
26 #include "mysqlnd_wireprotocol.h"
27 #include "mysqlnd_statistics.h"
28 #include "mysqlnd_debug.h"
29 #include "mysqlnd_ext_plugin.h"
30 #include "php_network.h"
31 #include "zend_ini.h"
32 #ifdef MYSQLND_COMPRESSION_ENABLED
33 #include <zlib.h>
34 #endif
35 
36 #ifndef PHP_WIN32
37 #include <netinet/tcp.h>
38 #else
39 #include <winsock.h>
40 #endif
41 
42 
43 /* {{{ mysqlnd_set_sock_no_delay */
44 static int
mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_DC)45 mysqlnd_set_sock_no_delay(php_stream * stream TSRMLS_DC)
46 {
47 
48 	int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
49 	int ret = SUCCESS;
50 	int flag = 1;
51 	int result = setsockopt(socketd, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int));
52 
53 	DBG_ENTER("mysqlnd_set_sock_no_delay");
54 
55 	if (result == -1) {
56 		ret = FAILURE;
57 	}
58 
59 	DBG_RETURN(ret);
60 }
61 /* }}} */
62 
63 
64 /* {{{ mysqlnd_net::network_read_ex */
65 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,network_read_ex)66 MYSQLND_METHOD(mysqlnd_net, network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
67 											 MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
68 {
69 	enum_func_status return_value = PASS;
70 	php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
71 	size_t old_chunk_size = net_stream->chunk_size;
72 	size_t to_read = count, ret;
73 	zend_uchar * p = buffer;
74 
75 	DBG_ENTER("mysqlnd_net::network_read_ex");
76 	DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
77 
78 	net_stream->chunk_size = MIN(to_read, net->data->options.net_read_buffer_size);
79 	while (to_read) {
80 		if (!(ret = php_stream_read(net_stream, (char *) p, to_read))) {
81 			DBG_ERR_FMT("Error while reading header from socket");
82 			return_value = FAIL;
83 			break;
84 		}
85 		p += ret;
86 		to_read -= ret;
87 	}
88 	MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
89 	net_stream->chunk_size = old_chunk_size;
90 	DBG_RETURN(return_value);
91 }
92 /* }}} */
93 
94 
95 /* {{{ mysqlnd_net::network_write_ex */
96 static size_t
MYSQLND_METHOD(mysqlnd_net,network_write_ex)97 MYSQLND_METHOD(mysqlnd_net, network_write_ex)(MYSQLND_NET * const net, const zend_uchar * const buffer, const size_t count,
98 											  MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
99 {
100 	size_t ret;
101 	DBG_ENTER("mysqlnd_net::network_write_ex");
102 	ret = php_stream_write(net->data->m.get_stream(net TSRMLS_CC), (char *)buffer, count);
103 	DBG_RETURN(ret);
104 }
105 /* }}} */
106 
107 /* {{{ mysqlnd_net::open_pipe */
108 static php_stream *
MYSQLND_METHOD(mysqlnd_net,open_pipe)109 MYSQLND_METHOD(mysqlnd_net, open_pipe)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
110 									   const zend_bool persistent,
111 									   MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
112 {
113 #if PHP_API_VERSION < 20100412
114 	unsigned int streams_options = ENFORCE_SAFE_MODE;
115 #else
116 	unsigned int streams_options = 0;
117 #endif
118 	php_stream * net_stream = NULL;
119 
120 	DBG_ENTER("mysqlnd_net::open_pipe");
121 	if (persistent) {
122 		streams_options |= STREAM_OPEN_PERSISTENT;
123 	}
124 	streams_options |= IGNORE_URL;
125 	net_stream = php_stream_open_wrapper((char*) scheme + sizeof("pipe://") - 1, "r+", streams_options, NULL);
126 	if (!net_stream) {
127 		SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "Unknown errror while connecting");
128 		DBG_RETURN(NULL);
129 	}
130 	/*
131 	  Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
132 	  be registered as resource (in EG(regular_list). So far, so good. However, it won't be
133 	  unregistered yntil the script ends. So, we need to take care of that.
134 	*/
135 	net_stream->in_free = 1;
136 	zend_hash_index_del(&EG(regular_list), net_stream->rsrc_id);
137 	net_stream->in_free = 0;
138 
139 
140 	DBG_RETURN(net_stream);
141 }
142 /* }}} */
143 
144 
145 /* {{{ mysqlnd_net::open_tcp_or_unix */
146 static php_stream *
MYSQLND_METHOD(mysqlnd_net,open_tcp_or_unix)147 MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
148 											  const zend_bool persistent,
149 											  MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
150 {
151 #if PHP_API_VERSION < 20100412
152 	unsigned int streams_options = ENFORCE_SAFE_MODE;
153 #else
154 	unsigned int streams_options = 0;
155 #endif
156 	unsigned int streams_flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
157 	char * hashed_details = NULL;
158 	int hashed_details_len = 0;
159 	char * errstr = NULL;
160 	int errcode = 0;
161 	struct timeval tv;
162 	php_stream * net_stream = NULL;
163 
164 	DBG_ENTER("mysqlnd_net::open_tcp_or_unix");
165 
166 	net->data->stream = NULL;
167 
168 	if (persistent) {
169 		hashed_details_len = mnd_sprintf(&hashed_details, 0, "%p", net);
170 		DBG_INF_FMT("hashed_details=%s", hashed_details);
171 	}
172 
173 	if (net->data->options.timeout_connect) {
174 		tv.tv_sec = net->data->options.timeout_connect;
175 		tv.tv_usec = 0;
176 	}
177 
178 	DBG_INF_FMT("calling php_stream_xport_create");
179 	net_stream = php_stream_xport_create(scheme, scheme_len, streams_options, streams_flags,
180 										  hashed_details, (net->data->options.timeout_connect) ? &tv : NULL,
181 										  NULL /*ctx*/, &errstr, &errcode);
182 	if (errstr || !net_stream) {
183 		DBG_ERR("Error");
184 		if (hashed_details) {
185 			mnd_sprintf_free(hashed_details);
186 		}
187 		errcode = CR_CONNECTION_ERROR;
188 		SET_CLIENT_ERROR(*error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr);
189 		if (errstr) {
190 			/* no mnd_ since we don't allocate it */
191 			efree(errstr);
192 		}
193 		DBG_RETURN(NULL);
194 	}
195 	if (hashed_details) {
196 		/*
197 		  If persistent, the streams register it in EG(persistent_list).
198 		  This is unwanted. ext/mysql or ext/mysqli are responsible to clean,
199 		  whatever they have to.
200 		*/
201 		zend_rsrc_list_entry *le;
202 
203 		if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_len + 1, (void*) &le) == SUCCESS) {
204 			/*
205 			  in_free will let streams code skip destructing - big HACK,
206 			  but STREAMS suck big time regarding persistent streams.
207 			  Just not compatible for extensions that need persistency.
208 			*/
209 			net_stream->in_free = 1;
210 			zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_len + 1);
211 			net_stream->in_free = 0;
212 		}
213 #if ZEND_DEBUG
214 		/* Shut-up the streams, they don't know what they are doing */
215 		net_stream->__exposed = 1;
216 #endif
217 		mnd_sprintf_free(hashed_details);
218 	}
219 
220 	/*
221 	  Streams are not meant for C extensions! Thus we need a hack. Every connected stream will
222 	  be registered as resource (in EG(regular_list). So far, so good. However, it won't be
223 	  unregistered yntil the script ends. So, we need to take care of that.
224 	*/
225 	net_stream->in_free = 1;
226 	zend_hash_index_del(&EG(regular_list), net_stream->rsrc_id);
227 	net_stream->in_free = 0;
228 
229 	DBG_RETURN(net_stream);
230 }
231 /* }}} */
232 
233 
234 /* {{{ mysqlnd_net::post_connect_set_opt */
235 static void
MYSQLND_METHOD(mysqlnd_net,post_connect_set_opt)236 MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt)(MYSQLND_NET * const net,
237 												  const char * const scheme, const size_t scheme_len,
238 												  MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
239 {
240 	php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
241 	DBG_ENTER("mysqlnd_net::post_connect_set_opt");
242 	if (net_stream) {
243 		if (net->data->options.timeout_read) {
244 			struct timeval tv;
245 			DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
246 			tv.tv_sec = net->data->options.timeout_read;
247 			tv.tv_usec = 0;
248 			php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
249 		}
250 
251 		if (!memcmp(scheme, "tcp://", sizeof("tcp://") - 1)) {
252 			/* TCP -> Set TCP_NODELAY */
253 			mysqlnd_set_sock_no_delay(net_stream TSRMLS_CC);
254 		}
255 	}
256 
257 	DBG_VOID_RETURN;
258 }
259 /* }}} */
260 
261 
262 /* {{{ mysqlnd_net::get_open_stream */
263 static func_mysqlnd_net__open_stream
MYSQLND_METHOD(mysqlnd_net,get_open_stream)264 MYSQLND_METHOD(mysqlnd_net, get_open_stream)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
265 											 MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
266 {
267 	func_mysqlnd_net__open_stream ret = NULL;
268 	DBG_ENTER("mysqlnd_net::get_open_stream");
269 	if (scheme_len > (sizeof("pipe://") - 1) && !memcmp(scheme, "pipe://", sizeof("pipe://") - 1)) {
270 		ret = net->data->m.open_pipe;
271 	} else if ((scheme_len > (sizeof("tcp://") - 1) && !memcmp(scheme, "tcp://", sizeof("tcp://") - 1))
272 				||
273 				(scheme_len > (sizeof("unix://") - 1) && !memcmp(scheme, "unix://", sizeof("unix://") - 1)))
274 	{
275 		ret = net->data->m.open_tcp_or_unix;
276 	}
277 
278 	if (!ret) {
279 		SET_CLIENT_ERROR(*error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, "No handler for this scheme");
280 	}
281 
282 	DBG_RETURN(ret);
283 }
284 /* }}} */
285 
286 
287 /* {{{ mysqlnd_net::connect_ex */
288 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,connect_ex)289 MYSQLND_METHOD(mysqlnd_net, connect_ex)(MYSQLND_NET * const net, const char * const scheme, const size_t scheme_len,
290 										const zend_bool persistent,
291 										MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
292 {
293 	enum_func_status ret = FAIL;
294 	func_mysqlnd_net__open_stream open_stream = NULL;
295 	DBG_ENTER("mysqlnd_net::connect_ex");
296 
297 	net->packet_no = net->compressed_envelope_packet_no = 0;
298 
299 	net->data->m.close_stream(net, conn_stats, error_info TSRMLS_CC);
300 
301 	open_stream = net->data->m.get_open_stream(net, scheme, scheme_len, error_info TSRMLS_CC);
302 	if (open_stream) {
303 		php_stream * net_stream = open_stream(net, scheme, scheme_len, persistent, conn_stats, error_info TSRMLS_CC);
304 		if (net_stream) {
305 			(void) net->data->m.set_stream(net, net_stream TSRMLS_CC);
306 			net->data->m.post_connect_set_opt(net, scheme, scheme_len, conn_stats, error_info TSRMLS_CC);
307 			ret = PASS;
308 		}
309 	}
310 
311 	DBG_RETURN(ret);
312 }
313 /* }}} */
314 
315 
316 /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
317 #define COPY_HEADER(T,A)  do { \
318 		*(((char *)(T)))   = *(((char *)(A)));\
319 		*(((char *)(T))+1) = *(((char *)(A))+1);\
320 		*(((char *)(T))+2) = *(((char *)(A))+2);\
321 		*(((char *)(T))+3) = *(((char *)(A))+3); } while (0)
322 #define STORE_HEADER_SIZE(safe_storage, buffer)  COPY_HEADER((safe_storage), (buffer))
323 #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
324 
325 
326 /* {{{ mysqlnd_net::send_ex */
327 /*
328   IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!!
329 			  This is done for performance reasons in the caller of this function.
330 			  Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
331 			  Neither are quick, thus the clients of this function are obligated to do
332 			  what they are asked for.
333 
334   `count` is actually the length of the payload data. Thus :
335   count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer)
336 */
337 static size_t
MYSQLND_METHOD(mysqlnd_net,send_ex)338 MYSQLND_METHOD(mysqlnd_net, send_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
339 									 MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
340 {
341 	zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
342 	zend_uchar * safe_storage = safe_buf;
343 	size_t bytes_sent, packets_sent = 1;
344 	size_t left = count;
345 	zend_uchar * p = (zend_uchar *) buffer;
346 	zend_uchar * compress_buf = NULL;
347 	size_t to_be_sent;
348 
349 	DBG_ENTER("mysqlnd_net::send_ex");
350 	DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->data->compressed);
351 
352 	if (net->data->compressed == TRUE) {
353 		size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
354 		DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size);
355 		compress_buf = mnd_emalloc(comp_buf_size);
356 	}
357 
358 	do {
359 		to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
360 #ifdef MYSQLND_COMPRESSION_ENABLED
361 		if (net->data->compressed == TRUE) {
362 			/* here we need to compress the data and then write it, first comes the compressed header */
363 			size_t tmp_complen = to_be_sent;
364 			size_t payload_size;
365 			zend_uchar * uncompressed_payload = p; /* should include the header */
366 
367 			STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
368 			int3store(uncompressed_payload, to_be_sent);
369 			int1store(uncompressed_payload + 3, net->packet_no);
370 			if (PASS == net->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen,
371 									   uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC))
372 			{
373 				int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
374 				payload_size = tmp_complen;
375 			} else {
376 				int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
377 				memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
378 				payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
379 			}
380 			RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
381 
382 			int3store(compress_buf, payload_size);
383 			int1store(compress_buf + 3, net->packet_no);
384 			DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
385 			bytes_sent = net->data->m.network_write_ex(net, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE,
386 												 conn_stats, error_info TSRMLS_CC);
387 			net->compressed_envelope_packet_no++;
388   #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
389 			if (res == Z_OK) {
390 				size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
391 				zend_uchar * decompressed_data = mnd_malloc(decompressed_size);
392 				int error = net->data->m.decode(decompressed_data, decompressed_size,
393 										  compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
394 				if (error == Z_OK) {
395 					int i;
396 					DBG_INF("success decompressing");
397 					for (i = 0 ; i < decompressed_size; i++) {
398 						if (i && (i % 30 == 0)) {
399 							printf("\n\t\t");
400 						}
401 						printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
402 						DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
403 					}
404 				} else {
405 					DBG_INF("error decompressing");
406 				}
407 				mnd_free(decompressed_data);
408 			}
409   #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
410 		} else
411 #endif /* MYSQLND_COMPRESSION_ENABLED */
412 		{
413 			DBG_INF("no compression");
414 			STORE_HEADER_SIZE(safe_storage, p);
415 			int3store(p, to_be_sent);
416 			int1store(p + 3, net->packet_no);
417 			bytes_sent = net->data->m.network_write_ex(net, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC);
418 			RESTORE_HEADER_SIZE(p, safe_storage);
419 			net->compressed_envelope_packet_no++;
420 		}
421 		net->packet_no++;
422 
423 		p += to_be_sent;
424 		left -= to_be_sent;
425 		packets_sent++;
426 		/*
427 		  if left is 0 then there is nothing more to send, but if the last packet was exactly
428 		  with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
429 		  empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
430 		  indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
431 		  packet will be sent and this loop will end.
432 		*/
433 	} while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
434 
435 	DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no);
436 
437 	MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats,
438 			STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
439 			STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
440 			STAT_PACKETS_SENT, packets_sent);
441 
442 	if (compress_buf) {
443 		mnd_efree(compress_buf);
444 	}
445 
446 	/* Even for zero size payload we have to send a packet */
447 	if (!bytes_sent) {
448 		DBG_ERR_FMT("Can't %u send bytes", count);
449 		SET_CLIENT_ERROR(*error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
450 	}
451 	DBG_RETURN(bytes_sent);
452 }
453 /* }}} */
454 
455 
456 #ifdef MYSQLND_COMPRESSION_ENABLED
457 /* {{{ php_mysqlnd_read_buffer_is_empty */
458 static zend_bool
php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)459 php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
460 {
461 	return buffer->len? FALSE:TRUE;
462 }
463 /* }}} */
464 
465 
466 /* {{{ php_mysqlnd_read_buffer_read */
467 static void
php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer,size_t count,zend_uchar * dest)468 php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
469 {
470 	if (buffer->len >= count) {
471 		memcpy(dest, buffer->data + buffer->offset, count);
472 		buffer->offset += count;
473 		buffer->len -= count;
474 	}
475 }
476 /* }}} */
477 
478 
479 /* {{{ php_mysqlnd_read_buffer_bytes_left */
480 static size_t
php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)481 php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
482 {
483 	return buffer->len;
484 }
485 /* }}} */
486 
487 
488 /* {{{ php_mysqlnd_read_buffer_free */
489 static void
php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)490 php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
491 {
492 	DBG_ENTER("php_mysqlnd_read_buffer_free");
493 	if (*buffer) {
494 		mnd_efree((*buffer)->data);
495 		mnd_efree(*buffer);
496 		*buffer = NULL;
497 	}
498 	DBG_VOID_RETURN;
499 }
500 /* }}} */
501 
502 
503 /* {{{ php_mysqlnd_create_read_buffer */
504 static MYSQLND_READ_BUFFER *
mysqlnd_create_read_buffer(size_t count TSRMLS_DC)505 mysqlnd_create_read_buffer(size_t count TSRMLS_DC)
506 {
507 	MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
508 	DBG_ENTER("mysqlnd_create_read_buffer");
509 	ret->is_empty = php_mysqlnd_read_buffer_is_empty;
510 	ret->read = php_mysqlnd_read_buffer_read;
511 	ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
512 	ret->free_buffer = php_mysqlnd_read_buffer_free;
513 	ret->data = mnd_emalloc(count);
514 	ret->size = ret->len = count;
515 	ret->offset = 0;
516 	DBG_RETURN(ret);
517 }
518 /* }}} */
519 
520 
521 /* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */
522 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,read_compressed_packet_from_stream_and_fill_read_buffer)523 MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer)
524 		(MYSQLND_NET * net, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
525 {
526 	size_t decompressed_size;
527 	enum_func_status retval = PASS;
528 	zend_uchar * compressed_data = NULL;
529 	zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
530 	DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer");
531 
532 	/* Read the compressed header */
533 	if (FAIL == net->data->m.network_read_ex(net, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) {
534 		DBG_RETURN(FAIL);
535 	}
536 	decompressed_size = uint3korr(comp_header);
537 
538 	/* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
539 	/* we need to decompress the data */
540 
541 	if (decompressed_size) {
542 		compressed_data = mnd_emalloc(net_payload_size);
543 		if (FAIL == net->data->m.network_read_ex(net, compressed_data, net_payload_size, conn_stats, error_info TSRMLS_CC)) {
544 			retval = FAIL;
545 			goto end;
546 		}
547 		net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC);
548 		retval = net->data->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size TSRMLS_CC);
549 		if (FAIL == retval) {
550 			goto end;
551 		}
552 	} else {
553 		DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
554 		net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC);
555 		if (FAIL == net->data->m.network_read_ex(net, net->uncompressed_data->data, net_payload_size, conn_stats, error_info TSRMLS_CC)) {
556 			retval = FAIL;
557 			goto end;
558 		}
559 	}
560 end:
561 	if (compressed_data) {
562 		mnd_efree(compressed_data);
563 	}
564 	DBG_RETURN(retval);
565 }
566 /* }}} */
567 #endif /* MYSQLND_COMPRESSION_ENABLED */
568 
569 
570 /* {{{ mysqlnd_net::decode */
571 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,decode)572 MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len,
573 									const zend_uchar * const compressed_data, const size_t compressed_data_len TSRMLS_DC)
574 {
575 #ifdef MYSQLND_COMPRESSION_ENABLED
576 	int error;
577 	uLongf tmp_complen = uncompressed_data_len;
578 	DBG_ENTER("mysqlnd_net::decode");
579 	error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len);
580 
581 	DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len);
582 	if (error != Z_OK) {
583 		DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
584 	}
585 	DBG_RETURN(error == Z_OK? PASS:FAIL);
586 #else
587 	DBG_ENTER("mysqlnd_net::decode");
588 	DBG_RETURN(FAIL);
589 #endif
590 }
591 /* }}} */
592 
593 
594 /* {{{ mysqlnd_net::encode */
595 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,encode)596 MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len,
597 									const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len TSRMLS_DC)
598 {
599 #ifdef MYSQLND_COMPRESSION_ENABLED
600 	int error;
601 	uLongf tmp_complen = *compress_buffer_len;
602 	DBG_ENTER("mysqlnd_net::encode");
603 	error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len);
604 
605 	if (error != Z_OK) {
606 		DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
607 	} else {
608 		*compress_buffer_len = tmp_complen;
609 		DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen);
610 	}
611 
612 	DBG_RETURN(error == Z_OK? PASS:FAIL);
613 #else
614 	DBG_ENTER("mysqlnd_net::encode");
615 	DBG_RETURN(FAIL);
616 #endif
617 }
618 /* }}} */
619 
620 
621 /* {{{ mysqlnd_net::receive_ex */
622 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,receive_ex)623 MYSQLND_METHOD(mysqlnd_net, receive_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
624 										MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
625 {
626 	size_t to_read = count;
627 	zend_uchar * p = buffer;
628 
629 	DBG_ENTER("mysqlnd_net::receive_ex");
630 #ifdef MYSQLND_COMPRESSION_ENABLED
631 	if (net->data->compressed) {
632 		if (net->uncompressed_data) {
633 			size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
634 			DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer);
635 			if (to_read_from_buffer) {
636 				net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
637 				p += to_read_from_buffer;
638 				to_read -= to_read_from_buffer;
639 			}
640 			DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read);
641 			if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
642 				/* Everything was consumed. This should never happen here, but for security */
643 				net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
644 			}
645 		}
646 		if (to_read) {
647 			zend_uchar net_header[MYSQLND_HEADER_SIZE];
648 			size_t net_payload_size;
649 			zend_uchar packet_no;
650 
651 			if (FAIL == net->data->m.network_read_ex(net, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info TSRMLS_CC)) {
652 				DBG_RETURN(FAIL);
653 			}
654 			net_payload_size = uint3korr(net_header);
655 			packet_no = uint1korr(net_header + 3);
656 			if (net->compressed_envelope_packet_no != packet_no) {
657 				DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
658 							net->compressed_envelope_packet_no, packet_no, net_payload_size);
659 
660 				php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
661 						  net->compressed_envelope_packet_no, packet_no, net_payload_size);
662 				DBG_RETURN(FAIL);
663 			}
664 			net->compressed_envelope_packet_no++;
665 #ifdef MYSQLND_DUMP_HEADER_N_BODY
666 			DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (unsigned long) net_payload_size);
667 #endif
668 			/* Now let's read from the wire, decompress it and fill the read buffer */
669 			net->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(net, net_payload_size, conn_stats, error_info TSRMLS_CC);
670 
671 			/*
672 			  Now a bit of recursion - read from the read buffer,
673 			  if the data which we have just read from the wire
674 			  is not enough, then the recursive call will try to
675 			  satisfy it until it is satisfied.
676 			*/
677 			DBG_RETURN(net->data->m.receive_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC));
678 		}
679 		DBG_RETURN(PASS);
680 	}
681 #endif /* MYSQLND_COMPRESSION_ENABLED */
682 	DBG_RETURN(net->data->m.network_read_ex(net, p, to_read, conn_stats, error_info TSRMLS_CC));
683 }
684 /* }}} */
685 
686 
687 /* {{{ mysqlnd_net::set_client_option */
688 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,set_client_option)689 MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum mysqlnd_option option, const char * const value TSRMLS_DC)
690 {
691 	DBG_ENTER("mysqlnd_net::set_client_option");
692 	DBG_INF_FMT("option=%u", option);
693 	switch (option) {
694 		case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
695 			DBG_INF("MYSQLND_OPT_NET_CMD_BUFFER_SIZE");
696 			if (*(unsigned int*) value < MYSQLND_NET_CMD_BUFFER_MIN_SIZE) {
697 				DBG_RETURN(FAIL);
698 			}
699 			net->cmd_buffer.length = *(unsigned int*) value;
700 			DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->cmd_buffer.length);
701 			if (!net->cmd_buffer.buffer) {
702 				net->cmd_buffer.buffer = mnd_pemalloc(net->cmd_buffer.length, net->persistent);
703 			} else {
704 				net->cmd_buffer.buffer = mnd_perealloc(net->cmd_buffer.buffer, net->cmd_buffer.length, net->persistent);
705 			}
706 			break;
707 		case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
708 			DBG_INF("MYSQLND_OPT_NET_READ_BUFFER_SIZE");
709 			net->data->options.net_read_buffer_size = *(unsigned int*) value;
710 			DBG_INF_FMT("new_length="MYSQLND_SZ_T_SPEC, net->data->options.net_read_buffer_size);
711 			break;
712 		case MYSQL_OPT_CONNECT_TIMEOUT:
713 			DBG_INF("MYSQL_OPT_CONNECT_TIMEOUT");
714 			net->data->options.timeout_connect = *(unsigned int*) value;
715 			break;
716 		case MYSQLND_OPT_SSL_KEY:
717 			{
718 				zend_bool pers = net->persistent;
719 				if (net->data->options.ssl_key) {
720 					mnd_pefree(net->data->options.ssl_key, pers);
721 				}
722 				net->data->options.ssl_key = value? mnd_pestrdup(value, pers) : NULL;
723 				break;
724 			}
725 		case MYSQLND_OPT_SSL_CERT:
726 			{
727 				zend_bool pers = net->persistent;
728 				if (net->data->options.ssl_cert) {
729 					mnd_pefree(net->data->options.ssl_cert, pers);
730 				}
731 				net->data->options.ssl_cert = value? mnd_pestrdup(value, pers) : NULL;
732 				break;
733 			}
734 		case MYSQLND_OPT_SSL_CA:
735 			{
736 				zend_bool pers = net->persistent;
737 				if (net->data->options.ssl_ca) {
738 					mnd_pefree(net->data->options.ssl_ca, pers);
739 				}
740 				net->data->options.ssl_ca = value? mnd_pestrdup(value, pers) : NULL;
741 				break;
742 			}
743 		case MYSQLND_OPT_SSL_CAPATH:
744 			{
745 				zend_bool pers = net->persistent;
746 				if (net->data->options.ssl_capath) {
747 					mnd_pefree(net->data->options.ssl_capath, pers);
748 				}
749 				net->data->options.ssl_capath = value? mnd_pestrdup(value, pers) : NULL;
750 				break;
751 			}
752 		case MYSQLND_OPT_SSL_CIPHER:
753 			{
754 				zend_bool pers = net->persistent;
755 				if (net->data->options.ssl_cipher) {
756 					mnd_pefree(net->data->options.ssl_cipher, pers);
757 				}
758 				net->data->options.ssl_cipher = value? mnd_pestrdup(value, pers) : NULL;
759 				break;
760 			}
761 		case MYSQLND_OPT_SSL_PASSPHRASE:
762 			{
763 				zend_bool pers = net->persistent;
764 				if (net->data->options.ssl_passphrase) {
765 					mnd_pefree(net->data->options.ssl_passphrase, pers);
766 				}
767 				net->data->options.ssl_passphrase = value? mnd_pestrdup(value, pers) : NULL;
768 				break;
769 			}
770 		case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
771 			net->data->options.ssl_verify_peer = value? ((*(zend_bool *)value)? TRUE:FALSE): FALSE;
772 			break;
773 		case MYSQL_OPT_READ_TIMEOUT:
774 			net->data->options.timeout_read = *(unsigned int*) value;
775 			break;
776 #ifdef WHEN_SUPPORTED_BY_MYSQLI
777 		case MYSQL_OPT_WRITE_TIMEOUT:
778 			net->data->options.timeout_write = *(unsigned int*) value;
779 			break;
780 #endif
781 		case MYSQL_OPT_COMPRESS:
782 			net->data->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION;
783 			break;
784 		case MYSQL_SERVER_PUBLIC_KEY:
785 			{
786 				zend_bool pers = net->persistent;
787 				if (net->data->options.sha256_server_public_key) {
788 					mnd_pefree(net->data->options.sha256_server_public_key, pers);
789 				}
790 				net->data->options.sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL;
791 				break;
792 			}
793 		default:
794 			DBG_RETURN(FAIL);
795 	}
796 	DBG_RETURN(PASS);
797 }
798 /* }}} */
799 
800 /* {{{ mysqlnd_net::consume_uneaten_data */
801 size_t
MYSQLND_METHOD(mysqlnd_net,consume_uneaten_data)802 MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data)(MYSQLND_NET * const net, enum php_mysqlnd_server_command cmd TSRMLS_DC)
803 {
804 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
805 	/*
806 	  Switch to non-blocking mode and try to consume something from
807 	  the line, if possible, then continue. This saves us from looking for
808 	  the actual place where out-of-order packets have been sent.
809 	  If someone is completely sure that everything is fine, he can switch it
810 	  off.
811 	*/
812 	char tmp_buf[256];
813 	size_t skipped_bytes = 0;
814 	int opt = PHP_STREAM_OPTION_BLOCKING;
815 	php_stream * net_stream = net->data->get_stream(net TSRMLS_CC);
816 	int was_blocked = net_stream->ops->set_option(net_stream, opt, 0, NULL TSRMLS_CC);
817 
818 	DBG_ENTER("mysqlnd_net::consume_uneaten_data");
819 
820 	if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
821 		/* Do a read of 1 byte */
822 		int bytes_consumed;
823 
824 		do {
825 			skipped_bytes += (bytes_consumed = php_stream_read(net_stream, tmp_buf, sizeof(tmp_buf)));
826 		} while (bytes_consumed == sizeof(tmp_buf));
827 
828 		if (was_blocked) {
829 			net_stream->ops->set_option(net_stream, opt, 1, NULL TSRMLS_CC);
830 		}
831 
832 		if (bytes_consumed) {
833 			DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
834 						bytes_consumed, mysqlnd_command_to_text[net->last_command]);
835 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
836 							 "consumed all the output from the server",
837 							 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
838 		}
839 	}
840 	net->last_command = cmd;
841 
842 	DBG_RETURN(skipped_bytes);
843 #else
844 	return 0;
845 #endif
846 }
847 /* }}} */
848 
849 /*
850   in libmyusql, if cert and !key then key=cert
851 */
852 /* {{{ mysqlnd_net::enable_ssl */
853 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,enable_ssl)854 MYSQLND_METHOD(mysqlnd_net, enable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
855 {
856 #ifdef MYSQLND_SSL_SUPPORTED
857 	php_stream_context * context = php_stream_context_alloc(TSRMLS_C);
858 	php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
859 
860 	DBG_ENTER("mysqlnd_net::enable_ssl");
861 	if (!context) {
862 		DBG_RETURN(FAIL);
863 	}
864 
865 	if (net->data->options.ssl_key) {
866 		zval key_zval;
867 		ZVAL_STRING(&key_zval, net->data->options.ssl_key, 0);
868 		php_stream_context_set_option(context, "ssl", "local_pk", &key_zval);
869 	}
870 	if (net->data->options.ssl_verify_peer) {
871 		zval verify_peer_zval;
872 		ZVAL_TRUE(&verify_peer_zval);
873 		php_stream_context_set_option(context, "ssl", "verify_peer", &verify_peer_zval);
874 	}
875 	if (net->data->options.ssl_cert) {
876 		zval cert_zval;
877 		ZVAL_STRING(&cert_zval, net->data->options.ssl_cert, 0);
878 		php_stream_context_set_option(context, "ssl", "local_cert", &cert_zval);
879 		if (!net->data->options.ssl_key) {
880 			php_stream_context_set_option(context, "ssl", "local_pk", &cert_zval);
881 		}
882 	}
883 	if (net->data->options.ssl_ca) {
884 		zval cafile_zval;
885 		ZVAL_STRING(&cafile_zval, net->data->options.ssl_ca, 0);
886 		php_stream_context_set_option(context, "ssl", "cafile", &cafile_zval);
887 	}
888 	if (net->data->options.ssl_capath) {
889 		zval capath_zval;
890 		ZVAL_STRING(&capath_zval, net->data->options.ssl_capath, 0);
891 		php_stream_context_set_option(context, "ssl", "cafile", &capath_zval);
892 	}
893 	if (net->data->options.ssl_passphrase) {
894 		zval passphrase_zval;
895 		ZVAL_STRING(&passphrase_zval, net->data->options.ssl_passphrase, 0);
896 		php_stream_context_set_option(context, "ssl", "passphrase", &passphrase_zval);
897 	}
898 	if (net->data->options.ssl_cipher) {
899 		zval cipher_zval;
900 		ZVAL_STRING(&cipher_zval, net->data->options.ssl_cipher, 0);
901 		php_stream_context_set_option(context, "ssl", "ciphers", &cipher_zval);
902 	}
903 	php_stream_context_set(net_stream, context);
904 	if (php_stream_xport_crypto_setup(net_stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL TSRMLS_CC) < 0 ||
905 	    php_stream_xport_crypto_enable(net_stream, 1 TSRMLS_CC) < 0)
906 	{
907 		DBG_ERR("Cannot connect to MySQL by using SSL");
908 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot connect to MySQL by using SSL");
909 		DBG_RETURN(FAIL);
910 	}
911 	net->data->ssl = TRUE;
912 	/*
913 	  get rid of the context. we are persistent and if this is a real pconn used by mysql/mysqli,
914 	  then the context would not survive cleaning of EG(regular_list), where it is registered, as a
915 	  resource. What happens is that after this destruction any use of the network will mean usage
916 	  of the context, which means usage of already freed memory, bad. Actually we don't need this
917 	  context anymore after we have enabled SSL on the connection. Thus it is very simple, we remove it.
918 	*/
919 	php_stream_context_set(net_stream, NULL);
920 
921 	if (net->data->options.timeout_read) {
922 		struct timeval tv;
923 		DBG_INF_FMT("setting %u as PHP_STREAM_OPTION_READ_TIMEOUT", net->data->options.timeout_read);
924 		tv.tv_sec = net->data->options.timeout_read;
925 		tv.tv_usec = 0;
926 		php_stream_set_option(net_stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv);
927 	}
928 
929 	DBG_RETURN(PASS);
930 #else
931 	DBG_ENTER("mysqlnd_net::enable_ssl");
932 	DBG_INF("MYSQLND_SSL_SUPPORTED is not defined");
933 	DBG_RETURN(PASS);
934 #endif
935 }
936 /* }}} */
937 
938 
939 /* {{{ mysqlnd_net::disable_ssl */
940 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,disable_ssl)941 MYSQLND_METHOD(mysqlnd_net, disable_ssl)(MYSQLND_NET * const net TSRMLS_DC)
942 {
943 	DBG_ENTER("mysqlnd_net::disable_ssl");
944 	DBG_RETURN(PASS);
945 }
946 /* }}} */
947 
948 
949 /* {{{ mysqlnd_net::free_contents */
950 static void
MYSQLND_METHOD(mysqlnd_net,free_contents)951 MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net TSRMLS_DC)
952 {
953 	zend_bool pers = net->persistent;
954 	DBG_ENTER("mysqlnd_net::free_contents");
955 
956 #ifdef MYSQLND_COMPRESSION_ENABLED
957 	if (net->uncompressed_data) {
958 		net->uncompressed_data->free_buffer(&net->uncompressed_data TSRMLS_CC);
959 	}
960 #endif
961 	if (net->data->options.ssl_key) {
962 		mnd_pefree(net->data->options.ssl_key, pers);
963 		net->data->options.ssl_key = NULL;
964 	}
965 	if (net->data->options.ssl_cert) {
966 		mnd_pefree(net->data->options.ssl_cert, pers);
967 		net->data->options.ssl_cert = NULL;
968 	}
969 	if (net->data->options.ssl_ca) {
970 		mnd_pefree(net->data->options.ssl_ca, pers);
971 		net->data->options.ssl_ca = NULL;
972 	}
973 	if (net->data->options.ssl_capath) {
974 		mnd_pefree(net->data->options.ssl_capath, pers);
975 		net->data->options.ssl_capath = NULL;
976 	}
977 	if (net->data->options.ssl_cipher) {
978 		mnd_pefree(net->data->options.ssl_cipher, pers);
979 		net->data->options.ssl_cipher = NULL;
980 	}
981 	if (net->data->options.sha256_server_public_key) {
982 		mnd_pefree(net->data->options.sha256_server_public_key, pers);
983 		net->data->options.sha256_server_public_key = NULL;
984 	}
985 
986 	DBG_VOID_RETURN;
987 }
988 /* }}} */
989 
990 
991 /* {{{ mysqlnd_net::close_stream */
992 static void
MYSQLND_METHOD(mysqlnd_net,close_stream)993 MYSQLND_METHOD(mysqlnd_net, close_stream)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
994 {
995 	php_stream * net_stream;
996 	DBG_ENTER("mysqlnd_net::close_stream");
997 	if (net && (net_stream = net->data->m.get_stream(net TSRMLS_CC))) {
998 		zend_bool pers = net->persistent;
999 		DBG_INF_FMT("Freeing stream. abstract=%p", net_stream->abstract);
1000 		if (pers) {
1001 			if (EG(active)) {
1002 				php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
1003 			} else {
1004 				/*
1005 				  otherwise we will crash because the EG(persistent_list) has been freed already,
1006 				  before the modules are shut down
1007 				*/
1008 				php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR);
1009 			}
1010 		} else {
1011 			php_stream_free(net_stream, PHP_STREAM_FREE_CLOSE);
1012 		}
1013 		(void) net->data->m.set_stream(net, NULL TSRMLS_CC);
1014 	}
1015 
1016 	DBG_VOID_RETURN;
1017 }
1018 /* }}} */
1019 
1020 
1021 /* {{{ mysqlnd_net::init */
1022 static enum_func_status
MYSQLND_METHOD(mysqlnd_net,init)1023 MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
1024 {
1025 	unsigned int buf_size;
1026 	DBG_ENTER("mysqlnd_net::init");
1027 
1028 	buf_size = MYSQLND_G(net_cmd_buffer_size); /* this is long, cast to unsigned int*/
1029 	net->data->m.set_client_option(net, MYSQLND_OPT_NET_CMD_BUFFER_SIZE, (char *) &buf_size TSRMLS_CC);
1030 
1031 	buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to unsigned int*/
1032 	net->data->m.set_client_option(net, MYSQLND_OPT_NET_READ_BUFFER_SIZE, (char *)&buf_size TSRMLS_CC);
1033 
1034 	buf_size = MYSQLND_G(net_read_timeout); /* this is long, cast to unsigned int*/
1035 	net->data->m.set_client_option(net, MYSQL_OPT_READ_TIMEOUT, (char *)&buf_size TSRMLS_CC);
1036 
1037 	DBG_RETURN(PASS);
1038 }
1039 /* }}} */
1040 
1041 
1042 /* {{{ mysqlnd_net::dtor */
1043 static void
MYSQLND_METHOD(mysqlnd_net,dtor)1044 MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info TSRMLS_DC)
1045 {
1046 	DBG_ENTER("mysqlnd_net::dtor");
1047 	if (net) {
1048 		net->data->m.free_contents(net TSRMLS_CC);
1049 		net->data->m.close_stream(net, stats, error_info TSRMLS_CC);
1050 
1051 		if (net->cmd_buffer.buffer) {
1052 			DBG_INF("Freeing cmd buffer");
1053 			mnd_pefree(net->cmd_buffer.buffer, net->persistent);
1054 			net->cmd_buffer.buffer = NULL;
1055 		}
1056 
1057 		mnd_pefree(net->data, net->data->persistent);
1058 		mnd_pefree(net, net->persistent);
1059 	}
1060 	DBG_VOID_RETURN;
1061 }
1062 /* }}} */
1063 
1064 
1065 /* {{{ mysqlnd_net::get_stream */
1066 static php_stream *
MYSQLND_METHOD(mysqlnd_net,get_stream)1067 MYSQLND_METHOD(mysqlnd_net, get_stream)(const MYSQLND_NET * const net TSRMLS_DC)
1068 {
1069 	DBG_ENTER("mysqlnd_net::get_stream");
1070 	DBG_INF_FMT("%p", net? net->data->stream:NULL);
1071 	DBG_RETURN(net? net->data->stream:NULL);
1072 }
1073 /* }}} */
1074 
1075 
1076 /* {{{ mysqlnd_net::set_stream */
1077 static php_stream *
MYSQLND_METHOD(mysqlnd_net,set_stream)1078 MYSQLND_METHOD(mysqlnd_net, set_stream)(MYSQLND_NET * const net, php_stream * net_stream TSRMLS_DC)
1079 {
1080 	php_stream * ret = NULL;
1081 	DBG_ENTER("mysqlnd_net::set_stream");
1082 	if (net) {
1083 		net->data->stream = net_stream;
1084 		ret = net->data->stream;
1085 	}
1086 	DBG_RETURN(ret);
1087 }
1088 /* }}} */
1089 
1090 
1091 MYSQLND_CLASS_METHODS_START(mysqlnd_net)
1092 	MYSQLND_METHOD(mysqlnd_net, init),
1093 	MYSQLND_METHOD(mysqlnd_net, dtor),
1094 	MYSQLND_METHOD(mysqlnd_net, connect_ex),
1095 	MYSQLND_METHOD(mysqlnd_net, close_stream),
1096 	MYSQLND_METHOD(mysqlnd_net, open_pipe),
1097 	MYSQLND_METHOD(mysqlnd_net, open_tcp_or_unix),
1098 	MYSQLND_METHOD(mysqlnd_net, get_stream),
1099 	MYSQLND_METHOD(mysqlnd_net, set_stream),
1100 	MYSQLND_METHOD(mysqlnd_net, get_open_stream),
1101 	MYSQLND_METHOD(mysqlnd_net, post_connect_set_opt),
1102 	MYSQLND_METHOD(mysqlnd_net, set_client_option),
1103 	MYSQLND_METHOD(mysqlnd_net, decode),
1104 	MYSQLND_METHOD(mysqlnd_net, encode),
1105 	MYSQLND_METHOD(mysqlnd_net, consume_uneaten_data),
1106 	MYSQLND_METHOD(mysqlnd_net, free_contents),
1107 	MYSQLND_METHOD(mysqlnd_net, enable_ssl),
1108 	MYSQLND_METHOD(mysqlnd_net, disable_ssl),
1109 	MYSQLND_METHOD(mysqlnd_net, network_read_ex),
1110 	MYSQLND_METHOD(mysqlnd_net, network_write_ex),
1111 	MYSQLND_METHOD(mysqlnd_net, send_ex),
1112 	MYSQLND_METHOD(mysqlnd_net, receive_ex),
1113 #ifdef MYSQLND_COMPRESSION_ENABLED
1114 	MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer),
1115 #else
1116 	NULL,
1117 #endif
1118 	NULL, /* unused 1 */
1119 	NULL, /* unused 2 */
1120 	NULL, /* unused 3 */
1121 	NULL, /* unused 4 */
1122 	NULL  /* unused 5 */
1123 MYSQLND_CLASS_METHODS_END;
1124 
1125 
1126 /* {{{ mysqlnd_net_init */
1127 PHPAPI MYSQLND_NET *
mysqlnd_net_init(zend_bool persistent,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info TSRMLS_DC)1128 mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
1129 {
1130 	MYSQLND_NET * net;
1131 	DBG_ENTER("mysqlnd_net_init");
1132 	net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_io_channel(persistent, stats, error_info TSRMLS_CC);
1133 	DBG_RETURN(net);
1134 }
1135 /* }}} */
1136 
1137 
1138 /* {{{ mysqlnd_net_free */
1139 PHPAPI void
mysqlnd_net_free(MYSQLND_NET * const net,MYSQLND_STATS * stats,MYSQLND_ERROR_INFO * error_info TSRMLS_DC)1140 mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info TSRMLS_DC)
1141 {
1142 	DBG_ENTER("mysqlnd_net_free");
1143 	if (net) {
1144 		net->data->m.dtor(net, stats, error_info TSRMLS_CC);
1145 	}
1146 	DBG_VOID_RETURN;
1147 }
1148 /* }}} */
1149 
1150 
1151 
1152 /*
1153  * Local variables:
1154  * tab-width: 4
1155  * c-basic-offset: 4
1156  * End:
1157  * vim600: noet sw=4 ts=4 fdm=marker
1158  * vim<600: noet sw=4 ts=4
1159  */
1160