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