1 :
2 : /*
3 : +----------------------------------------------------------------------+
4 : | PHP Version 6 |
5 : +----------------------------------------------------------------------+
6 : | Copyright (c) 2006-2009 The PHP Group |
7 : +----------------------------------------------------------------------+
8 : | This source file is subject to version 3.01 of the PHP license, |
9 : | that is bundled with this package in the file LICENSE, and is |
10 : | available through the world-wide-web at the following url: |
11 : | http://www.php.net/license/3_01.txt |
12 : | If you did not receive a copy of the PHP license and are unable to |
13 : | obtain it through the world-wide-web, please send a note to |
14 : | license@php.net so we can mail you a copy immediately. |
15 : +----------------------------------------------------------------------+
16 : | Authors: Georg Richter <georg@mysql.com> |
17 : | Andrey Hristov <andrey@mysql.com> |
18 : | Ulf Wendel <uwendel@mysql.com> |
19 : +----------------------------------------------------------------------+
20 : */
21 : #include "php.h"
22 : #include "php_globals.h"
23 : #include "mysqlnd.h"
24 : #include "mysqlnd_priv.h"
25 : #include "mysqlnd_wireprotocol.h"
26 : #include "mysqlnd_statistics.h"
27 : #include "mysqlnd_palloc.h"
28 : #include "mysqlnd_debug.h"
29 : #include "mysqlnd_block_alloc.h"
30 : #include "ext/standard/sha1.h"
31 : #include "php_network.h"
32 : #include "zend_ini.h"
33 : #ifdef MYSQLND_COMPRESSION_ENABLED
34 : #include <zlib.h>
35 : #endif
36 :
37 : #ifndef PHP_WIN32
38 : #include <netinet/tcp.h>
39 : #else
40 : #include <winsock.h>
41 : #endif
42 :
43 : #define MYSQLND_SILENT 1
44 :
45 : #define MYSQLND_DUMP_HEADER_N_BODY
46 :
47 :
48 : #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
49 : { \
50 : DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
51 : if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
52 : CONN_SET_STATE(conn, CONN_QUIT_SENT); \
53 : SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
54 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
55 : DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
56 : DBG_RETURN(FAIL);\
57 : }\
58 : if ((buf_size) < (packet)->header.size) { \
59 : DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
60 : (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
61 : DBG_RETURN(FAIL); \
62 : }\
63 : if (FAIL == mysqlnd_read_body((conn), &((packet)->header), (buf) TSRMLS_CC)) { \
64 : CONN_SET_STATE(conn, CONN_QUIT_SENT); \
65 : SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
66 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
67 : DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
68 : DBG_RETURN(FAIL);\
69 : } \
70 : MYSQLND_INC_CONN_STATISTIC_W_VALUE2(&conn->stats, packet_type_to_statistic_byte_count[packet_type], \
71 : MYSQLND_HEADER_SIZE + (packet)->header.size, \
72 : packet_type_to_statistic_packet_count[packet_type], \
73 : 1); \
74 : }
75 :
76 :
77 : extern mysqlnd_packet_methods packet_methods[];
78 :
79 : static const char *unknown_sqlstate= "HY000";
80 :
81 : char * const mysqlnd_empty_string = "";
82 :
83 : /* Used in mysqlnd_debug.c */
84 : char * mysqlnd_read_header_name = "mysqlnd_read_header";
85 : char * mysqlnd_read_body_name = "mysqlnd_read_body";
86 :
87 :
88 : /* {{{ mysqlnd_command_to_text
89 : */
90 : const char * const mysqlnd_command_to_text[COM_END] =
91 : {
92 : "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
93 : "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
94 : "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
95 : "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
96 : "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
97 : "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
98 : "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
99 : };
100 : /* }}} */
101 :
102 :
103 :
104 : static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
105 : {
106 : STAT_LAST,
107 : STAT_LAST,
108 : STAT_BYTES_RECEIVED_OK,
109 : STAT_BYTES_RECEIVED_EOF,
110 : STAT_LAST,
111 : STAT_BYTES_RECEIVED_RSET_HEADER,
112 : STAT_BYTES_RECEIVED_RSET_FIELD_META,
113 : STAT_BYTES_RECEIVED_RSET_ROW,
114 : STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
115 : STAT_BYTES_RECEIVED_CHANGE_USER,
116 : };
117 :
118 : static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
119 : {
120 : STAT_LAST,
121 : STAT_LAST,
122 : STAT_PACKETS_RECEIVED_OK,
123 : STAT_PACKETS_RECEIVED_EOF,
124 : STAT_LAST,
125 : STAT_PACKETS_RECEIVED_RSET_HEADER,
126 : STAT_PACKETS_RECEIVED_RSET_FIELD_META,
127 : STAT_PACKETS_RECEIVED_RSET_ROW,
128 : STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
129 : STAT_PACKETS_RECEIVED_CHANGE_USER,
130 : };
131 :
132 :
133 : /* {{{ php_mysqlnd_net_field_length
134 : Get next field's length */
135 : unsigned long php_mysqlnd_net_field_length(zend_uchar **packet)
136 230005 : {
137 230005 : register zend_uchar *p= (zend_uchar *)*packet;
138 :
139 230005 : if (*p < 251) {
140 225076 : (*packet)++;
141 225076 : return (unsigned long) *p;
142 : }
143 :
144 4929 : switch (*p) {
145 : case 251:
146 747 : (*packet)++;
147 747 : return MYSQLND_NULL_LENGTH;
148 : case 252:
149 4169 : (*packet) += 3;
150 4169 : return (unsigned long) uint2korr(p+1);
151 : case 253:
152 12 : (*packet) += 4;
153 12 : return (unsigned long) uint3korr(p+1);
154 : default:
155 1 : (*packet) += 9;
156 1 : return (unsigned long) uint4korr(p+1);
157 : }
158 : }
159 : /* }}} */
160 :
161 :
162 : /* {{{ php_mysqlnd_net_field_length_ll
163 : Get next field's length */
164 : uint64_t php_mysqlnd_net_field_length_ll(zend_uchar **packet)
165 28546 : {
166 28546 : register zend_uchar *p= (zend_uchar *)*packet;
167 :
168 28546 : if (*p < 251) {
169 28539 : (*packet)++;
170 28539 : return (uint64_t) *p;
171 : }
172 :
173 7 : switch (*p) {
174 : case 251:
175 0 : (*packet)++;
176 0 : return (uint64_t) MYSQLND_NULL_LENGTH;
177 : case 252:
178 4 : (*packet) += 3;
179 4 : return (uint64_t) uint2korr(p + 1);
180 : case 253:
181 0 : (*packet) += 4;
182 0 : return (uint64_t) uint3korr(p + 1);
183 : default:
184 3 : (*packet) += 9;
185 3 : return (uint64_t) uint8korr(p + 1);
186 : }
187 : }
188 : /* }}} */
189 :
190 :
191 : /* {{{ php_mysqlnd_net_store_length */
192 : zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
193 608 : {
194 608 : if (length < (uint64_t) L64(251)) {
195 541 : *packet = (zend_uchar) length;
196 541 : return packet + 1;
197 : }
198 :
199 67 : if (length < (uint64_t) L64(65536)) {
200 63 : *packet++ = 252;
201 63 : int2store(packet,(unsigned int) length);
202 63 : return packet + 2;
203 : }
204 :
205 4 : if (length < (uint64_t) L64(16777216)) {
206 4 : *packet++ = 253;
207 4 : int3store(packet,(ulong) length);
208 4 : return packet + 3;
209 : }
210 0 : *packet++ = 254;
211 0 : int8store(packet, length);
212 0 : return packet + 8;
213 : }
214 : /* }}} */
215 :
216 :
217 : #ifdef MYSQLND_COMPRESSION_ENABLED
218 : /* {{{ php_mysqlnd_read_buffer_is_empty */
219 : static zend_bool
220 : php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
221 0 : {
222 0 : return buffer->len? FALSE:TRUE;
223 : }
224 : /* }}} */
225 :
226 :
227 : /* {{{ php_mysqlnd_read_buffer_read */
228 : static void
229 : php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
230 0 : {
231 0 : if (buffer->len >= count) {
232 0 : memcpy(dest, buffer->data + buffer->offset, count);
233 0 : buffer->offset += count;
234 0 : buffer->len -= count;
235 : }
236 0 : }
237 : /* }}} */
238 :
239 :
240 : /* {{{ php_mysqlnd_read_buffer_bytes_left */
241 : static size_t
242 : php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
243 0 : {
244 0 : return buffer->len;
245 : }
246 : /* }}} */
247 :
248 :
249 : /* {{{ php_mysqlnd_read_buffer_free */
250 : static void
251 : php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
252 0 : {
253 0 : DBG_ENTER("php_mysqlnd_read_buffer_free");
254 0 : if (*buffer) {
255 0 : mnd_efree((*buffer)->data);
256 0 : mnd_efree(*buffer);
257 0 : *buffer = NULL;
258 : }
259 : DBG_VOID_RETURN;
260 : }
261 : /* }}} */
262 :
263 :
264 : /* {{{ php_mysqlnd_create_read_buffer */
265 : static MYSQLND_READ_BUFFER *
266 : mysqlnd_create_read_buffer(size_t count TSRMLS_DC)
267 0 : {
268 0 : MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
269 0 : DBG_ENTER("mysqlnd_create_read_buffer");
270 0 : ret->is_empty = php_mysqlnd_read_buffer_is_empty;
271 0 : ret->read = php_mysqlnd_read_buffer_read;
272 0 : ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
273 0 : ret->free = php_mysqlnd_read_buffer_free;
274 0 : ret->data = mnd_emalloc(count);
275 0 : ret->size = ret->len = count;
276 0 : ret->offset = 0;
277 0 : DBG_RETURN(ret);
278 : }
279 : /* }}} */
280 : #endif
281 :
282 :
283 : /* {{{ php_mysqlnd_consume_uneaten_data */
284 : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
285 : size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC)
286 : {
287 :
288 : /*
289 : Switch to non-blocking mode and try to consume something from
290 : the line, if possible, then continue. This saves us from looking for
291 : the actuall place where out-of-order packets have been sent.
292 : If someone is completely sure that everything is fine, he can switch it
293 : off.
294 : */
295 : char tmp_buf[256];
296 : MYSQLND_NET *net = &conn->net;
297 : size_t skipped_bytes = 0;
298 : int opt = PHP_STREAM_OPTION_BLOCKING;
299 : int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC);
300 :
301 : DBG_ENTER("php_mysqlnd_consume_uneaten_data");
302 :
303 : if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
304 : /* Do a read of 1 byte */
305 : int bytes_consumed;
306 :
307 : do {
308 : skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf)));
309 : } while (bytes_consumed == sizeof(tmp_buf));
310 :
311 : if (was_blocked) {
312 : net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC);
313 : }
314 :
315 : if (bytes_consumed) {
316 : DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
317 : bytes_consumed, mysqlnd_command_to_text[net->last_command]);
318 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
319 : "consumed all the output from the server",
320 : bytes_consumed, mysqlnd_command_to_text[net->last_command]);
321 : }
322 : }
323 : net->last_command = cmd;
324 :
325 : DBG_RETURN(skipped_bytes);
326 : }
327 : #endif
328 : /* }}} */
329 :
330 :
331 : /* {{{ php_mysqlnd_read_error_from_line */
332 : static enum_func_status
333 : php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
334 : char *error, int error_buf_len,
335 : unsigned int *error_no, char *sqlstate TSRMLS_DC)
336 736 : {
337 736 : zend_uchar *p = buf;
338 736 : int error_msg_len= 0;
339 :
340 736 : DBG_ENTER("php_mysqlnd_read_error_from_line");
341 :
342 736 : if (buf_len > 2) {
343 736 : *error_no = uint2korr(p);
344 736 : p+= 2;
345 : /* sqlstate is following */
346 736 : if (*p == '#') {
347 736 : memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH);
348 736 : p+= MYSQLND_SQLSTATE_LENGTH;
349 : }
350 736 : error_msg_len = buf_len - (p - buf);
351 736 : error_msg_len = MIN(error_msg_len, error_buf_len - 1);
352 736 : memcpy(error, p, error_msg_len);
353 : } else {
354 0 : *error_no = CR_UNKNOWN_ERROR;
355 0 : memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
356 : }
357 736 : sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
358 736 : error[error_msg_len]= '\0';
359 :
360 736 : DBG_RETURN(FAIL);
361 : }
362 : /* }}} */
363 :
364 :
365 : /* {{{ mysqlnd_set_sock_no_delay */
366 : int mysqlnd_set_sock_no_delay(php_stream *stream)
367 0 : {
368 :
369 0 : int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
370 0 : int ret = SUCCESS;
371 0 : int flag = 1;
372 0 : int result = setsockopt(socketd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
373 : TSRMLS_FETCH();
374 :
375 0 : DBG_ENTER("mysqlnd_set_sock_no_delay");
376 :
377 0 : if (result == -1) {
378 0 : ret = FAILURE;
379 : }
380 :
381 0 : DBG_RETURN(ret);
382 : }
383 : /* }}} */
384 :
385 :
386 : /* {{{ mysqlnd_stream_write */
387 : size_t mysqlnd_stream_write(MYSQLND * const conn, const zend_uchar * const buf, size_t count TSRMLS_DC)
388 31257 : {
389 : size_t ret;
390 31257 : DBG_ENTER("mysqlnd_stream_write");
391 31257 : ret = php_stream_write(conn->net.stream, (char *)buf, count);
392 31257 : DBG_RETURN(ret);
393 : }
394 : /* }}} */
395 :
396 :
397 : /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
398 : #define STORE_HEADER_SIZE(safe_storage, buffer) int4store((safe_storage), (*(uint32_t *)(buffer)))
399 : #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
400 :
401 : /* {{{ mysqlnd_stream_write_w_header */
402 : /*
403 : IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!!
404 : This is done for performance reasons in the caller of this function.
405 : Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
406 : Neither are quick, thus the clients of this function are obligated to do
407 : what they are asked for.
408 :
409 : `count` is actually the length of the payload data. Thus :
410 : count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer)
411 : */
412 : size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC)
413 31257 : {
414 : zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
415 31257 : zend_uchar *safe_storage = safe_buf;
416 31257 : MYSQLND_NET *net = &conn->net;
417 31257 : size_t old_chunk_size = net->stream->chunk_size;
418 31257 : size_t ret, left = count, packets_sent = 1;
419 31257 : zend_uchar *p = (zend_uchar *) buf;
420 31257 : zend_uchar * compress_buf = NULL;
421 31257 : size_t comp_buf_size = 0;
422 :
423 31257 : DBG_ENTER("mysqlnd_stream_write_w_header");
424 31257 : DBG_INF_FMT("conn=%llu count=%lu compression=%d", conn->thread_id, count, net->compressed);
425 :
426 31257 : net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
427 :
428 31257 : if (net->compressed == TRUE) {
429 0 : comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
430 0 : DBG_INF_FMT("compress_buf_size=%d", comp_buf_size);
431 0 : compress_buf = emalloc(comp_buf_size);
432 : }
433 :
434 62514 : while (left > MYSQLND_MAX_PACKET_SIZE) {
435 : #ifdef MYSQLND_COMPRESSION_ENABLED
436 :
437 0 : if (net->compressed == TRUE) {
438 :
439 : /* here we need to compress the data and then write it, first comes the compressed header */
440 0 : uLong tmp_complen = MYSQLND_MAX_PACKET_SIZE;
441 : size_t payload_size;
442 0 : zend_uchar * uncompressed_payload = p; /* should include the header */
443 : int res;
444 :
445 0 : STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
446 0 : int3store(uncompressed_payload, MYSQLND_MAX_PACKET_SIZE);
447 0 : int1store(uncompressed_payload + 3, net->packet_no);
448 :
449 0 : DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
450 0 : res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
451 0 : if (res == Z_OK) {
452 0 : DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
453 0 : int3store(compress_buf + MYSQLND_HEADER_SIZE, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
454 0 : payload_size = tmp_complen;
455 : } else {
456 0 : DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
457 0 : int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
458 0 : memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
459 0 : payload_size = MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE;
460 : }
461 0 : RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
462 :
463 0 : int3store(compress_buf, payload_size);
464 0 : int1store(compress_buf + 3, net->packet_no);
465 0 : DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
466 0 : ret = conn->net.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
467 0 : net->compressed_envelope_packet_no++;
468 : } else
469 : #endif /* MYSQLND_COMPRESSION_ENABLED */
470 : {
471 0 : DBG_INF("no compression");
472 0 : STORE_HEADER_SIZE(safe_storage, p);
473 0 : int3store(p, MYSQLND_MAX_PACKET_SIZE);
474 0 : int1store(p + 3, net->packet_no);
475 0 : ret = conn->net.stream_write(conn, p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE TSRMLS_CC);
476 0 : RESTORE_HEADER_SIZE(p, safe_storage);
477 0 : net->compressed_envelope_packet_no++;
478 : }
479 0 : net->packet_no++;
480 :
481 0 : p += MYSQLND_MAX_PACKET_SIZE;
482 0 : left -= MYSQLND_MAX_PACKET_SIZE;
483 :
484 0 : packets_sent++;
485 : }
486 :
487 31257 : DBG_INF_FMT("packet_size=%d packet_no=%d", left, net->packet_no);
488 : /* Even for zero size payload we have to send a packet */
489 :
490 : #ifdef MYSQLND_COMPRESSION_ENABLED
491 31257 : if (net->compressed == TRUE) {
492 : /* here we need to compress the data and then write it, first comes the compressed header */
493 0 : uLong tmp_complen = left;
494 : size_t payload_size;
495 0 : zend_uchar * uncompressed_payload = p; /* should include the header */
496 0 : int res = Z_BUF_ERROR;
497 :
498 0 : STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
499 0 : int3store(uncompressed_payload, left);
500 0 : int1store(uncompressed_payload + 3, net->packet_no);
501 0 : if ((left + MYSQLND_HEADER_SIZE) > MYSQLND_MIN_COMPRESS_LEN) {
502 0 : DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, left + MYSQLND_HEADER_SIZE);
503 0 : res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, left + MYSQLND_HEADER_SIZE);
504 0 : if (res == Z_OK) {
505 0 : DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
506 0 : int3store(compress_buf + MYSQLND_HEADER_SIZE, left + MYSQLND_HEADER_SIZE);
507 0 : payload_size = tmp_complen;
508 : }
509 : } else {
510 0 : DBG_INF("Too short for compression");
511 : }
512 0 : if (res != Z_OK) {
513 0 : DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
514 0 : int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
515 0 : memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, left + MYSQLND_HEADER_SIZE);
516 0 : payload_size = left + MYSQLND_HEADER_SIZE;
517 : }
518 0 : RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
519 :
520 0 : int3store(compress_buf, payload_size);
521 0 : int1store(compress_buf + 3, net->packet_no);
522 0 : DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
523 0 : ret = conn->net.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
524 :
525 0 : net->compressed_envelope_packet_no++;
526 : #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
527 : if (res == Z_OK) {
528 : size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
529 : uLongf tmp_complen2 = decompressed_size;
530 : zend_uchar * decompressed_data = malloc(decompressed_size);
531 : int error = uncompress(decompressed_data, &tmp_complen2, compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
532 : if (error == Z_OK) {
533 : int i;
534 : DBG_INF("success decompressing");
535 : for (i = 0 ; i < decompressed_size; i++) {
536 : if (i && (i % 30 == 0)) {
537 : printf("\n\t\t");
538 : }
539 : printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
540 : DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
541 : }
542 : } else {
543 : DBG_INF("error decompressing");
544 : }
545 : free(decompressed_data);
546 : }
547 : #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
548 :
549 : } else
550 : #endif /* MYSQLND_COMPRESSION_ENABLED */
551 : {
552 31257 : DBG_INF("no compression");
553 31257 : STORE_HEADER_SIZE(safe_storage, p);
554 31257 : int3store(p, left);
555 31257 : int1store(p + 3, net->packet_no);
556 31257 : ret = conn->net.stream_write(conn, p, left + MYSQLND_HEADER_SIZE TSRMLS_CC);
557 31257 : RESTORE_HEADER_SIZE(p, safe_storage);
558 : }
559 31257 : net->packet_no++;
560 :
561 31257 : if (!ret) {
562 8 : DBG_ERR_FMT("Can't %u send bytes", count);
563 8 : conn->state = CONN_QUIT_SENT;
564 8 : SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
565 : }
566 :
567 31257 : MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats,
568 : STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
569 : STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
570 : STAT_PACKETS_SENT, packets_sent);
571 :
572 31257 : net->stream->chunk_size = old_chunk_size;
573 31257 : if (compress_buf) {
574 0 : efree(compress_buf);
575 : }
576 31257 : DBG_RETURN(ret);
577 : }
578 : /* }}} */
579 :
580 :
581 : /* {{{ mysqlnd_read_from_stream */
582 : enum_func_status
583 : mysqlnd_read_from_stream(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
584 191232 : {
585 191232 : size_t to_read = count, ret;
586 191232 : size_t old_chunk_size = conn->net.stream->chunk_size;
587 191232 : DBG_ENTER("mysqlnd_read_from_stream");
588 191232 : DBG_INF_FMT("count=%u", count);
589 191232 : conn->net.stream->chunk_size = MIN(to_read, conn->options.net_read_buffer_size);
590 573746 : while (to_read) {
591 191286 : if (!(ret = php_stream_read(conn->net.stream, (char *) buffer, to_read))) {
592 4 : DBG_ERR_FMT("Error while reading header from socket");
593 4 : DBG_RETURN(FAIL);
594 : }
595 191282 : buffer += ret;
596 191282 : to_read -= ret;
597 : }
598 191228 : MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, count);
599 191228 : conn->net.stream->chunk_size = old_chunk_size;
600 191228 : DBG_RETURN(PASS);
601 : }
602 : /* }}} */
603 :
604 :
605 : #ifdef MYSQLND_COMPRESSION_ENABLED
606 : /* {{{ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer */
607 : static enum_func_status
608 : mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, size_t net_payload_size TSRMLS_DC)
609 0 : {
610 0 : MYSQLND_NET * net = &conn->net;
611 : size_t decompressed_size;
612 0 : enum_func_status ret = PASS;
613 0 : zend_uchar * compressed_data = NULL;
614 : zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
615 0 : DBG_ENTER("mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer");
616 :
617 : /* Read the compressed header */
618 0 : if (FAIL == conn->net.stream_read(conn, comp_header, COMPRESSED_HEADER_SIZE TSRMLS_CC)) {
619 0 : DBG_RETURN(FAIL);
620 : }
621 0 : decompressed_size = uint3korr(comp_header);
622 :
623 : /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
624 : /* we need to decompress the data */
625 :
626 0 : if (decompressed_size) {
627 : int error;
628 0 : uLongf tmp_complen = decompressed_size;
629 0 : compressed_data = emalloc(net_payload_size);
630 0 : if (FAIL == conn->net.stream_read(conn, compressed_data, net_payload_size TSRMLS_CC)) {
631 0 : ret = FAIL;
632 0 : goto end;
633 : }
634 :
635 0 : net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC);
636 0 : error = uncompress(net->uncompressed_data->data, &tmp_complen, compressed_data, net_payload_size);
637 :
638 0 : DBG_INF_FMT("compressed data: decomp_len=%d compressed_size=%d", decompressed_size, net_payload_size);
639 0 : if (error != Z_OK) {
640 0 : DBG_ERR_FMT("Can't uncompress packet, error: %d", error);
641 0 : ret = FAIL;
642 0 : goto end;
643 : }
644 : } else {
645 0 : DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
646 0 : net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC);
647 0 : if (FAIL == conn->net.stream_read(conn, net->uncompressed_data->data, net_payload_size TSRMLS_CC)) {
648 0 : ret = FAIL;
649 0 : goto end;
650 : }
651 : }
652 0 : end:
653 0 : if (compressed_data) {
654 0 : efree(compressed_data);
655 : }
656 0 : DBG_RETURN(ret);
657 : }
658 : #endif /* MYSQLND_COMPRESSION_ENABLED */
659 :
660 :
661 : /* {{{ mysqlnd_real_read */
662 : static enum_func_status
663 : mysqlnd_real_read(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
664 191232 : {
665 191232 : size_t to_read = count;
666 191232 : zend_uchar * p = buffer;
667 :
668 191232 : DBG_ENTER("mysqlnd_real_read");
669 : #ifdef MYSQLND_COMPRESSION_ENABLED
670 191232 : if (conn->net.compressed) {
671 0 : MYSQLND_NET * net = &conn->net;
672 0 : if (net->uncompressed_data) {
673 0 : size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
674 0 : DBG_INF_FMT("reading %u from uncompressed_data buffer", to_read_from_buffer);
675 0 : if (to_read_from_buffer) {
676 0 : net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
677 0 : p += to_read_from_buffer;
678 0 : to_read -= to_read_from_buffer;
679 : }
680 0 : DBG_INF_FMT("left %u to read", to_read);
681 0 : if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
682 : /* Everything was consumed. This should never happen here, but for security */
683 0 : net->uncompressed_data->free(&net->uncompressed_data TSRMLS_CC);
684 : }
685 : }
686 0 : if (to_read) {
687 : zend_uchar net_header[MYSQLND_HEADER_SIZE];
688 : size_t net_payload_size;
689 : zend_uchar packet_no;
690 :
691 0 : if (FAIL == conn->net.stream_read(conn, net_header, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
692 0 : DBG_RETURN(FAIL);
693 : }
694 0 : net_payload_size = uint3korr(net_header);
695 0 : packet_no = uint1korr(net_header + 3);
696 0 : if (net->compressed_envelope_packet_no != packet_no) {
697 0 : DBG_ERR_FMT("Transport level: packets out of order. Expected %d received %d. Packet size=%d",
698 : net->compressed_envelope_packet_no, packet_no, net_payload_size);
699 :
700 0 : php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC,
701 : net->compressed_envelope_packet_no, packet_no, net_payload_size);
702 : #if 0
703 : *(int *) NULL = 0;
704 : #endif
705 0 : DBG_RETURN(FAIL);
706 : }
707 0 : net->compressed_envelope_packet_no++;
708 : #ifdef MYSQLND_DUMP_HEADER_N_BODY
709 0 : DBG_INF_FMT("HEADER: hwd_packet_no=%d size=%3d", packet_no, net_payload_size);
710 : #endif
711 : /* Now let's read from the wire, decompress it and fill the read buffer */
712 0 : mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(conn, net_payload_size TSRMLS_CC);
713 :
714 : /*
715 : Now a bit of recursion - read from the read buffer,
716 : if the data which we have just read from the wire
717 : is not enough, then the recursive call will try to
718 : satisfy it until it is satisfied.
719 : */
720 0 : DBG_RETURN(mysqlnd_real_read(conn, p, to_read TSRMLS_CC));
721 : }
722 0 : DBG_RETURN(PASS);
723 : }
724 : #endif /* MYSQLND_COMPRESSION_ENABLED */
725 191232 : DBG_RETURN(conn->net.stream_read(conn, p, to_read TSRMLS_CC));
726 : }
727 : /* }}} */
728 :
729 :
730 : /* {{{ mysqlnd_read_header */
731 : static enum_func_status
732 : mysqlnd_read_header(MYSQLND * conn, mysqlnd_packet_header * header TSRMLS_DC)
733 95619 : {
734 95619 : MYSQLND_NET *net = &conn->net;
735 : zend_uchar buffer[MYSQLND_HEADER_SIZE];
736 :
737 95619 : DBG_ENTER("mysqlnd_read_header_name");
738 95619 : DBG_INF_FMT("compressed=%d conn_id=%u", net->compressed, conn->thread_id);
739 95619 : if (FAIL == mysqlnd_real_read(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
740 4 : DBG_RETURN(FAIL);
741 : }
742 :
743 95615 : header->size = uint3korr(buffer);
744 95615 : header->packet_no = uint1korr(buffer + 3);
745 :
746 : #ifdef MYSQLND_DUMP_HEADER_N_BODY
747 95615 : DBG_INF_FMT("HEADER: prot_packet_no=%d size=%3d", header->packet_no, header->size);
748 : #endif
749 95615 : MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats,
750 : STAT_BYTES_RECEIVED, MYSQLND_HEADER_SIZE,
751 : STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
752 : STAT_PACKETS_RECEIVED, 1);
753 :
754 95615 : if (net->compressed || net->packet_no == header->packet_no) {
755 : /*
756 : Have to increase the number, so we can send correct number back. It will
757 : round at 255 as this is unsigned char. The server needs this for simple
758 : flow control checking.
759 : */
760 95613 : net->packet_no++;
761 95613 : DBG_RETURN(PASS);
762 : }
763 :
764 2 : DBG_ERR_FMT("Logical link: packets out of order. Expected %d received %d. Packet size=%d",
765 : net->packet_no, header->packet_no, header->size);
766 :
767 2 : php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC,
768 : net->packet_no, header->packet_no, header->size);
769 2 : DBG_RETURN(FAIL);
770 : }
771 : /* }}} */
772 :
773 :
774 : /* {{{ mysqlnd_read_body */
775 : static enum_func_status
776 : mysqlnd_read_body(MYSQLND *conn, mysqlnd_packet_header * header, zend_uchar * store_buf TSRMLS_DC)
777 95613 : {
778 95613 : MYSQLND_NET *net = &conn->net;
779 :
780 95613 : DBG_ENTER(mysqlnd_read_body_name);
781 95613 : DBG_INF_FMT("chunk_size=%d compression=%d", net->stream->chunk_size, net->compressed);
782 :
783 95613 : DBG_RETURN(mysqlnd_real_read(conn, store_buf, header->size TSRMLS_CC));
784 : }
785 : /* }}} */
786 :
787 :
788 : /* {{{ php_mysqlnd_greet_read */
789 : static enum_func_status
790 : php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
791 1625 : {
792 : zend_uchar buf[2048];
793 1625 : zend_uchar *p = buf;
794 1625 : zend_uchar *begin = buf;
795 1625 : php_mysql_packet_greet *packet= (php_mysql_packet_greet *) _packet;
796 :
797 1625 : DBG_ENTER("php_mysqlnd_greet_read");
798 :
799 1625 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
800 :
801 1625 : packet->protocol_version = uint1korr(p);
802 1625 : p++;
803 :
804 1625 : if (packet->protocol_version == 0xFF) {
805 0 : php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
806 : packet->error, sizeof(packet->error),
807 : &packet->error_no, packet->sqlstate
808 : TSRMLS_CC);
809 : /*
810 : The server doesn't send sqlstate in the greet packet.
811 : It's a bug#26426 , so we have to set it correctly ourselves.
812 : It's probably "Too many connections, which has SQL state 08004".
813 : */
814 0 : if (packet->error_no == 1040) {
815 0 : memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
816 : }
817 0 : DBG_RETURN(PASS);
818 : }
819 :
820 1625 : packet->server_version = estrdup((char *)p);
821 1625 : p+= strlen(packet->server_version) + 1; /* eat the '\0' */
822 :
823 1625 : packet->thread_id = uint4korr(p);
824 1625 : p+=4;
825 :
826 1625 : memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
827 1625 : p+= 8;
828 :
829 : /* pad1 */
830 1625 : p++;
831 :
832 1625 : packet->server_capabilities = uint2korr(p);
833 1625 : p+= 2;
834 :
835 1625 : packet->charset_no = uint1korr(p);
836 1625 : p++;
837 :
838 1625 : packet->server_status = uint2korr(p);
839 1625 : p+= 2;
840 :
841 : /* pad2 */
842 1625 : p+= 13;
843 :
844 1625 : if (p - buf < packet->header.size) {
845 : /* scramble_buf is split into two parts */
846 1625 : memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
847 : p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
848 : } else {
849 0 : packet->pre41 = TRUE;
850 : }
851 :
852 1625 : DBG_INF_FMT("proto=%d server=%s thread_id=%ld",
853 : packet->protocol_version, packet->server_version, packet->thread_id);
854 :
855 1625 : DBG_INF_FMT("server_capabilities=%d charset_no=%d server_status=%d",
856 : packet->server_capabilities, packet->charset_no, packet->server_status);
857 :
858 1625 : if (p - begin > packet->header.size) {
859 0 : DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
860 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
861 : p - begin - packet->header.size);
862 : }
863 :
864 1625 : DBG_RETURN(PASS);
865 : }
866 : /* }}} */
867 :
868 :
869 : /* {{{ php_mysqlnd_greet_free_mem */
870 : static
871 : void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
872 1634 : {
873 1634 : php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet;
874 1634 : if (p->server_version) {
875 1625 : mnd_efree(p->server_version);
876 1625 : p->server_version = NULL;
877 : }
878 1634 : if (!alloca) {
879 0 : mnd_efree(p);
880 : }
881 1634 : }
882 : /* }}} */
883 :
884 :
885 : #define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
886 : CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
887 : CLIENT_MULTI_RESULTS)
888 :
889 :
890 : /* {{{ php_mysqlnd_crypt */
891 : static
892 : void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
893 1671 : {
894 1671 : const zend_uchar *s1_end = s1 + len;
895 36762 : while (s1 < s1_end) {
896 33420 : *buffer++= *s1++ ^ *s2++;
897 : }
898 1671 : }
899 : /* }}} */
900 :
901 :
902 : /* {{{ php_mysqlnd_scramble */
903 : void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble,
904 : const zend_uchar * const password)
905 1671 : {
906 : PHP_SHA1_CTX context;
907 : zend_uchar sha1[SHA1_MAX_LENGTH];
908 : zend_uchar sha2[SHA1_MAX_LENGTH];
909 :
910 :
911 : /* Phase 1: hash password */
912 1671 : PHP_SHA1Init(&context);
913 1671 : PHP_SHA1Update(&context, password, strlen((char *)password));
914 1671 : PHP_SHA1Final(sha1, &context);
915 :
916 : /* Phase 2: hash sha1 */
917 1671 : PHP_SHA1Init(&context);
918 1671 : PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
919 1671 : PHP_SHA1Final(sha2, &context);
920 :
921 : /* Phase 3: hash scramble + sha2 */
922 1671 : PHP_SHA1Init(&context);
923 1671 : PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
924 1671 : PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
925 1671 : PHP_SHA1Final(buffer, &context);
926 :
927 : /* let's crypt buffer now */
928 1671 : php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
929 1671 : }
930 : /* }}} */
931 :
932 :
933 : /* {{{ php_mysqlnd_auth_write */
934 : static
935 : size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC)
936 1625 : {
937 : char buffer[1024];
938 1625 : register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
939 : int len;
940 1625 : register php_mysql_packet_auth *packet= (php_mysql_packet_auth *) _packet;
941 :
942 1625 : DBG_ENTER("php_mysqlnd_auth_write");
943 :
944 1625 : packet->client_flags |= MYSQLND_CAPABILITIES;
945 :
946 1625 : if (packet->db) {
947 1625 : packet->client_flags |= CLIENT_CONNECT_WITH_DB;
948 : }
949 :
950 1625 : if (PG(open_basedir) && strlen(PG(open_basedir))) {
951 6 : packet->client_flags ^= CLIENT_LOCAL_FILES;
952 : }
953 :
954 1625 : int4store(p, packet->client_flags);
955 1625 : p+= 4;
956 :
957 1625 : int4store(p, packet->max_packet_size);
958 1625 : p+= 4;
959 :
960 1625 : int1store(p, packet->charset_no);
961 1625 : p++;
962 :
963 1625 : memset(p, 0, 23); /* filler */
964 1625 : p+= 23;
965 :
966 1625 : len= strlen(packet->user);
967 1625 : memcpy(p, packet->user, len);
968 1625 : p+= len;
969 1625 : *p++ = '\0';
970 :
971 : /* copy scrambled pass*/
972 3232 : if (packet->password && packet->password[0]) {
973 : /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
974 1607 : int1store(p, 20);
975 1607 : p++;
976 1607 : php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password);
977 1607 : p+= 20;
978 : } else {
979 : /* Zero length */
980 18 : int1store(p, 0);
981 18 : p++;
982 : }
983 :
984 1625 : if (packet->db) {
985 1625 : memcpy(p, packet->db, packet->db_len);
986 1625 : p+= packet->db_len;
987 1625 : *p++= '\0';
988 : }
989 : /* Handle CLIENT_CONNECT_WITH_DB */
990 : /* no \0 for no DB */
991 :
992 1625 : DBG_RETURN(mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
993 : }
994 : /* }}} */
995 :
996 :
997 : /* {{{ php_mysqlnd_auth_free_mem */
998 : static
999 : void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
1000 1634 : {
1001 1634 : if (!alloca) {
1002 1634 : mnd_pefree((php_mysql_packet_auth *) _packet, ((php_mysql_packet_auth *)_packet)->header.persistent);
1003 : }
1004 1634 : }
1005 : /* }}} */
1006 :
1007 :
1008 : #define OK_BUFFER_SIZE 2048
1009 :
1010 : /* {{{ php_mysqlnd_ok_read */
1011 : static enum_func_status
1012 : php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1013 1897 : {
1014 : zend_uchar local_buf[OK_BUFFER_SIZE];
1015 1897 : size_t buf_len = conn->net.cmd_buffer.buffer? conn->net.cmd_buffer.length : OK_BUFFER_SIZE;
1016 1897 : zend_uchar *buf = conn->net.cmd_buffer.buffer? (zend_uchar *) conn->net.cmd_buffer.buffer : local_buf;
1017 1897 : zend_uchar *p = buf;
1018 1897 : zend_uchar *begin = buf;
1019 : unsigned long i;
1020 1897 : register php_mysql_packet_ok *packet= (php_mysql_packet_ok *) _packet;
1021 :
1022 1897 : DBG_ENTER("php_mysqlnd_ok_read");
1023 :
1024 1897 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
1025 :
1026 : /* Should be always 0x0 or 0xFF for error */
1027 1897 : packet->field_count = uint1korr(p);
1028 1897 : p++;
1029 :
1030 1897 : if (0xFF == packet->field_count) {
1031 43 : php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1032 : packet->error, sizeof(packet->error),
1033 : &packet->error_no, packet->sqlstate
1034 : TSRMLS_CC);
1035 43 : DBG_RETURN(PASS);
1036 : }
1037 : /* Everything was fine! */
1038 1854 : packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
1039 1854 : packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
1040 :
1041 1854 : packet->server_status = uint2korr(p);
1042 1854 : p+= 2;
1043 :
1044 1854 : packet->warning_count = uint2korr(p);
1045 1854 : p+= 2;
1046 :
1047 : /* There is a message */
1048 1860 : if (packet->header.size > p - buf && (i = php_mysqlnd_net_field_length(&p))) {
1049 6 : packet->message = estrndup((char *)p, MIN(i, buf_len - (p - begin)));
1050 6 : packet->message_len = i;
1051 : } else {
1052 1848 : packet->message = NULL;
1053 : }
1054 :
1055 1854 : DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d",
1056 : packet->affected_rows, packet->last_insert_id, packet->server_status,
1057 : packet->warning_count);
1058 :
1059 1854 : if (p - begin > packet->header.size) {
1060 0 : DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
1061 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
1062 : p - begin - packet->header.size);
1063 : }
1064 :
1065 1854 : DBG_RETURN(PASS);
1066 : }
1067 : /* }}} */
1068 :
1069 :
1070 : /* {{{ php_mysqlnd_ok_free_mem */
1071 : static
1072 : void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
1073 1906 : {
1074 1906 : php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet;
1075 1906 : if (p->message) {
1076 6 : mnd_efree(p->message);
1077 6 : p->message = NULL;
1078 : }
1079 1906 : if (!alloca) {
1080 0 : mnd_pefree(p, p->header.persistent);
1081 : }
1082 1906 : }
1083 : /* }}} */
1084 :
1085 :
1086 : /* {{{ php_mysqlnd_eof_read */
1087 : static enum_func_status
1088 : php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1089 9308 : {
1090 : /*
1091 : EOF packet is since 4.1 five bytes long,
1092 : but we can get also an error, make it bigger.
1093 :
1094 : Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
1095 : */
1096 9308 : php_mysql_packet_eof *packet= (php_mysql_packet_eof *) _packet;
1097 9308 : size_t buf_len = conn->net.cmd_buffer.length;
1098 9308 : zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
1099 9308 : zend_uchar *p = buf;
1100 9308 : zend_uchar *begin = buf;
1101 :
1102 9308 : DBG_ENTER("php_mysqlnd_eof_read");
1103 :
1104 9308 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
1105 :
1106 : /* Should be always 0xFE */
1107 9308 : packet->field_count = uint1korr(p);
1108 9308 : p++;
1109 :
1110 9308 : if (0xFF == packet->field_count) {
1111 2 : php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1112 : packet->error, sizeof(packet->error),
1113 : &packet->error_no, packet->sqlstate
1114 : TSRMLS_CC);
1115 2 : DBG_RETURN(PASS);
1116 : }
1117 :
1118 : /*
1119 : 4.1 sends 1 byte EOF packet after metadata of
1120 : PREPARE/EXECUTE but 5 bytes after the result. This is not
1121 : according to the Docs@Forge!!!
1122 : */
1123 9306 : if (packet->header.size > 1) {
1124 9306 : packet->warning_count = uint2korr(p);
1125 9306 : p+= 2;
1126 9306 : packet->server_status = uint2korr(p);
1127 9306 : p+= 2;
1128 : } else {
1129 0 : packet->warning_count = 0;
1130 0 : packet->server_status = 0;
1131 : }
1132 :
1133 9306 : if (p - begin > packet->header.size) {
1134 0 : DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
1135 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
1136 : p - begin - packet->header.size);
1137 : }
1138 :
1139 9306 : DBG_INF_FMT("EOF packet: fields=%d status=%d warnings=%d",
1140 : packet->field_count, packet->server_status, packet->warning_count);
1141 :
1142 9306 : DBG_RETURN(PASS);
1143 : }
1144 : /* }}} */
1145 :
1146 :
1147 : /* {{{ php_mysqlnd_eof_free_mem */
1148 : static
1149 : void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
1150 9308 : {
1151 9308 : if (!alloca) {
1152 0 : mnd_pefree(_packet, ((php_mysql_packet_eof *)_packet)->header.persistent);
1153 : }
1154 9308 : }
1155 : /* }}} */
1156 :
1157 :
1158 : /* {{{ php_mysqlnd_cmd_write */
1159 : size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
1160 29346 : {
1161 : /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
1162 29346 : php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet;
1163 29346 : MYSQLND_NET *net = &conn->net;
1164 29346 : unsigned int error_reporting = EG(error_reporting);
1165 : size_t written;
1166 :
1167 29346 : DBG_ENTER("php_mysqlnd_cmd_write");
1168 : /*
1169 : Reset packet_no, or we will get bad handshake!
1170 : Every command starts a new TX and packet numbers are reset to 0.
1171 : */
1172 29346 : net->packet_no = 0;
1173 29346 : net->compressed_envelope_packet_no = 0; /* this is for the response */
1174 :
1175 29346 : if (error_reporting) {
1176 28838 : EG(error_reporting) = 0;
1177 : }
1178 :
1179 29346 : MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_PACKETS_SENT_CMD);
1180 :
1181 : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
1182 : php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC);
1183 : #endif
1184 :
1185 30962 : if (!packet->argument || !packet->arg_len) {
1186 : char buffer[MYSQLND_HEADER_SIZE + 1];
1187 :
1188 1616 : int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
1189 1616 : written = mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC);
1190 : } else {
1191 27730 : size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
1192 : zend_uchar *tmp, *p;
1193 27730 : tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
1194 27730 : p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
1195 :
1196 27730 : int1store(p, packet->command);
1197 27730 : p++;
1198 :
1199 27730 : memcpy(p, packet->argument, packet->arg_len);
1200 :
1201 27730 : ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
1202 27730 : if (tmp != net->cmd_buffer.buffer) {
1203 30 : MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
1204 30 : mnd_efree(tmp);
1205 : }
1206 27730 : written = ret;
1207 : }
1208 29346 : if (error_reporting) {
1209 : /* restore error reporting */
1210 28838 : EG(error_reporting) = error_reporting;
1211 : }
1212 29346 : DBG_RETURN(written);
1213 : }
1214 : /* }}} */
1215 :
1216 :
1217 : /* {{{ php_mysqlnd_cmd_free_mem */
1218 : static
1219 : void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
1220 0 : {
1221 0 : if (!alloca) {
1222 0 : mnd_pefree(_packet, ((php_mysql_packet_command *)_packet)->header.persistent);
1223 : }
1224 0 : }
1225 : /* }}} */
1226 :
1227 :
1228 : /* {{{ php_mysqlnd_rset_header_read */
1229 : static enum_func_status
1230 : php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1231 19124 : {
1232 19124 : size_t buf_len = conn->net.cmd_buffer.length;
1233 19124 : zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
1234 19124 : zend_uchar *p = buf;
1235 19124 : zend_uchar *begin = buf;
1236 : size_t len;
1237 19124 : php_mysql_packet_rset_header *packet= (php_mysql_packet_rset_header *) _packet;
1238 :
1239 19124 : DBG_ENTER("php_mysqlnd_rset_header_read");
1240 :
1241 19124 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
1242 :
1243 : /*
1244 : Don't increment. First byte is 0xFF on error, but otherwise is starting byte
1245 : of encoded sequence for length.
1246 : */
1247 19121 : if (*p == 0xFF) {
1248 : /* Error */
1249 653 : p++;
1250 653 : php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1251 : packet->error_info.error, sizeof(packet->error_info.error),
1252 : &packet->error_info.error_no, packet->error_info.sqlstate
1253 : TSRMLS_CC);
1254 653 : DBG_RETURN(PASS);
1255 : }
1256 :
1257 18468 : packet->field_count= php_mysqlnd_net_field_length(&p);
1258 18468 : switch (packet->field_count) {
1259 : case MYSQLND_NULL_LENGTH:
1260 6 : DBG_INF("LOAD LOCAL");
1261 : /*
1262 : First byte in the packet is the field count.
1263 : Thus, the name is size - 1. And we add 1 for a trailing \0.
1264 : */
1265 6 : len = packet->header.size - 1;
1266 6 : packet->info_or_local_file = mnd_emalloc(len + 1);
1267 6 : memcpy(packet->info_or_local_file, p, len);
1268 6 : packet->info_or_local_file[len] = '\0';
1269 6 : packet->info_or_local_file_len = len;
1270 6 : break;
1271 : case 0x00:
1272 12419 : DBG_INF("UPSERT");
1273 12419 : packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
1274 12419 : packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p);
1275 12419 : packet->server_status = uint2korr(p);
1276 12419 : p+=2;
1277 12419 : packet->warning_count = uint2korr(p);
1278 12419 : p+=2;
1279 : /* Check for additional textual data */
1280 12419 : if (packet->header.size > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
1281 365 : packet->info_or_local_file = mnd_emalloc(len + 1);
1282 365 : memcpy(packet->info_or_local_file, p, len);
1283 365 : packet->info_or_local_file[len] = '\0';
1284 365 : packet->info_or_local_file_len = len;
1285 : }
1286 12419 : DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%d warning_count=%d",
1287 : packet->affected_rows, packet->last_insert_id,
1288 : packet->server_status, packet->warning_count);
1289 12419 : break;
1290 : default:
1291 6043 : DBG_INF("SELECT");
1292 : /* Result set */
1293 : break;
1294 : }
1295 18468 : if (p - begin > packet->header.size) {
1296 1 : DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
1297 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
1298 : p - begin - packet->header.size);
1299 : }
1300 :
1301 18468 : DBG_RETURN(PASS);
1302 : }
1303 : /* }}} */
1304 :
1305 :
1306 : /* {{{ php_mysqlnd_rset_header_free_mem */
1307 : static
1308 : void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
1309 19124 : {
1310 19124 : DBG_ENTER("php_mysqlnd_rset_header_free_mem");
1311 19124 : php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet;
1312 19124 : if (p->info_or_local_file) {
1313 371 : mnd_efree(p->info_or_local_file);
1314 371 : p->info_or_local_file = NULL;
1315 : }
1316 19124 : if (!alloca) {
1317 0 : mnd_pefree(p, p->header.persistent);
1318 : }
1319 : DBG_VOID_RETURN;
1320 : }
1321 : /* }}} */
1322 :
1323 : static size_t rset_field_offsets[] =
1324 : {
1325 : STRUCT_OFFSET(MYSQLND_FIELD, catalog),
1326 : STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
1327 : STRUCT_OFFSET(MYSQLND_FIELD, db),
1328 : STRUCT_OFFSET(MYSQLND_FIELD, db_length),
1329 : STRUCT_OFFSET(MYSQLND_FIELD, table),
1330 : STRUCT_OFFSET(MYSQLND_FIELD, table_length),
1331 : STRUCT_OFFSET(MYSQLND_FIELD, org_table),
1332 : STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
1333 : STRUCT_OFFSET(MYSQLND_FIELD, name),
1334 : STRUCT_OFFSET(MYSQLND_FIELD, name_length),
1335 : STRUCT_OFFSET(MYSQLND_FIELD, org_name),
1336 : STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
1337 : };
1338 :
1339 :
1340 : /* {{{ php_mysqlnd_rset_field_read */
1341 : static enum_func_status
1342 : php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1343 28966 : {
1344 : /* Should be enough for the metadata of a single row */
1345 28966 : php_mysql_packet_res_field *packet= (php_mysql_packet_res_field *) _packet;
1346 28966 : size_t buf_len = conn->net.cmd_buffer.length, total_len = 0;
1347 28966 : zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
1348 28966 : zend_uchar *p = buf;
1349 28966 : zend_uchar *begin = buf;
1350 : char *root_ptr;
1351 : unsigned long len;
1352 : MYSQLND_FIELD *meta;
1353 28966 : unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
1354 :
1355 28966 : DBG_ENTER("php_mysqlnd_rset_field_read");
1356 :
1357 28966 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
1358 :
1359 28965 : if (packet->skip_parsing) {
1360 1312 : DBG_RETURN(PASS);
1361 : }
1362 27653 : if (*p == 0xFF) {
1363 : /* Error */
1364 1 : p++;
1365 1 : php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
1366 : packet->error_info.error, sizeof(packet->error_info.error),
1367 : &packet->error_info.error_no, packet->error_info.sqlstate
1368 : TSRMLS_CC);
1369 1 : DBG_ERR_FMT("Server error : (%d) %s", packet->error_info.error_no, packet->error_info.error);
1370 1 : DBG_RETURN(PASS);
1371 27652 : } else if (*p == 0xFE && packet->header.size < 8) {
1372 : /* Premature EOF. That should be COM_FIELD_LIST */
1373 3 : DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
1374 3 : packet->stupid_list_fields_eof = TRUE;
1375 3 : DBG_RETURN(PASS);
1376 : }
1377 :
1378 27649 : meta = packet->metadata;
1379 :
1380 193543 : for (i = 0; i < field_count; i += 2) {
1381 165894 : len = php_mysqlnd_net_field_length(&p);
1382 165894 : switch ((len)) {
1383 : case 0:
1384 22783 : *(char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
1385 22783 : *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
1386 22783 : break;
1387 : case MYSQLND_NULL_LENGTH:
1388 0 : goto faulty_or_fake;
1389 : default:
1390 143111 : *(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p;
1391 143111 : *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
1392 143111 : p += len;
1393 143111 : total_len += len + 1;
1394 : break;
1395 : }
1396 : }
1397 :
1398 : /* 1 byte filler */
1399 27649 : p++;
1400 :
1401 27649 : meta->charsetnr = uint2korr(p);
1402 27649 : p += 2;
1403 :
1404 27649 : meta->length = uint4korr(p);
1405 27649 : p += 4;
1406 :
1407 27649 : meta->type = uint1korr(p);
1408 27649 : p += 1;
1409 :
1410 27649 : meta->flags = uint2korr(p);
1411 27649 : p += 2;
1412 :
1413 27649 : meta->decimals = uint2korr(p);
1414 27649 : p += 1;
1415 :
1416 : /* 2 byte filler */
1417 27649 : p +=2;
1418 :
1419 : /* Should we set NUM_FLAG (libmysql does it) ? */
1420 27649 : if (
1421 : (meta->type <= MYSQL_TYPE_INT24 &&
1422 : (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
1423 : ) || meta->type == MYSQL_TYPE_YEAR)
1424 : {
1425 11504 : meta->flags |= NUM_FLAG;
1426 : }
1427 :
1428 :
1429 : /*
1430 : def could be empty, thus don't allocate on the root.
1431 : NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
1432 : Otherwise the string is length encoded.
1433 : */
1434 27649 : if (packet->header.size > (p - buf) &&
1435 : (len = php_mysqlnd_net_field_length(&p)) &&
1436 : len != MYSQLND_NULL_LENGTH)
1437 : {
1438 3 : DBG_INF_FMT("Def found, length %lu", len);
1439 3 : meta->def = mnd_emalloc(len + 1);
1440 3 : memcpy(meta->def, p, len);
1441 3 : meta->def[len] = '\0';
1442 3 : meta->def_length = len;
1443 3 : p += len;
1444 : }
1445 :
1446 27649 : if (p - begin > packet->header.size) {
1447 0 : DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
1448 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
1449 : "shorter than expected", p - begin - packet->header.size);
1450 : }
1451 :
1452 27649 : root_ptr = meta->root = mnd_emalloc(total_len);
1453 27649 : meta->root_len = total_len;
1454 : /* Now do allocs */
1455 27649 : if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
1456 27649 : len = meta->catalog_length;
1457 27649 : meta->catalog = memcpy(root_ptr, meta->catalog, len);
1458 27649 : *(root_ptr +=len) = '\0';
1459 27649 : root_ptr++;
1460 : }
1461 :
1462 27649 : if (meta->db && meta->db != mysqlnd_empty_string) {
1463 21862 : len = meta->db_length;
1464 21862 : meta->db = memcpy(root_ptr, meta->db, len);
1465 21862 : *(root_ptr + len) = '\0';
1466 : }
1467 :
1468 27649 : if (meta->table && meta->table != mysqlnd_empty_string) {
1469 22029 : len = meta->table_length;
1470 22029 : meta->table = memcpy(root_ptr, meta->table, len);
1471 22029 : *(root_ptr +=len) = '\0';
1472 22029 : root_ptr++;
1473 : }
1474 :
1475 27649 : if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
1476 21862 : len = meta->org_table_length;
1477 21862 : meta->org_table = memcpy(root_ptr, meta->org_table, len);
1478 21862 : *(root_ptr +=len) = '\0';
1479 21862 : root_ptr++;
1480 : }
1481 :
1482 27649 : if (meta->name && meta->name != mysqlnd_empty_string) {
1483 27642 : len = meta->name_length;
1484 27642 : meta->name = memcpy(root_ptr, meta->name, len);
1485 27642 : *(root_ptr +=len) = '\0';
1486 27642 : root_ptr++;
1487 : }
1488 :
1489 27649 : if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
1490 22067 : len = meta->org_name_length;
1491 22067 : meta->org_name = memcpy(root_ptr, meta->org_name, len);
1492 22067 : *(root_ptr +=len) = '\0';
1493 22067 : root_ptr++;
1494 : }
1495 :
1496 27649 : DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
1497 : meta->name? meta->name:"*NA*");
1498 :
1499 27649 : DBG_RETURN(PASS);
1500 :
1501 0 : faulty_or_fake:
1502 0 : DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
1503 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
1504 : " The server is faulty");
1505 0 : DBG_RETURN(FAIL);
1506 : }
1507 : /* }}} */
1508 :
1509 :
1510 : /* {{{ php_mysqlnd_rset_field_free_mem */
1511 : static
1512 : void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
1513 9191 : {
1514 9191 : php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet;
1515 :
1516 : /* p->metadata was passed to us as temporal buffer */
1517 9191 : if (!alloca) {
1518 0 : mnd_pefree(p, p->header.persistent);
1519 : }
1520 9191 : }
1521 : /* }}} */
1522 :
1523 :
1524 : static enum_func_status
1525 : php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
1526 : MYSQLND_MEMORY_POOL_CHUNK **buffer,
1527 : uint64_t *data_size, zend_bool persistent_alloc,
1528 : unsigned int prealloc_more_bytes TSRMLS_DC)
1529 30496 : {
1530 30496 : enum_func_status ret = PASS;
1531 : mysqlnd_packet_header header;
1532 30496 : zend_uchar *p = NULL;
1533 30496 : zend_bool first_iteration = TRUE;
1534 :
1535 30496 : DBG_ENTER("php_mysqlnd_read_row_ex");
1536 :
1537 : /*
1538 : To ease the process the server splits everything in packets up to 2^24 - 1.
1539 : Even in the case the payload is evenly divisible by this value, the last
1540 : packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
1541 : for next one if they have 2^24 - 1 sizes. But just read the header of a
1542 : zero-length byte, don't read the body, there is no such.
1543 : */
1544 :
1545 30496 : *data_size = prealloc_more_bytes;
1546 : while (1) {
1547 30496 : if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) {
1548 1 : ret = FAIL;
1549 1 : break;
1550 : }
1551 :
1552 30495 : *data_size += header.size;
1553 :
1554 30495 : if (first_iteration) {
1555 30495 : first_iteration = FALSE;
1556 : /*
1557 : We need a trailing \0 for the last string, in case of text-mode,
1558 : to be able to implement read-only variables. Thus, we add + 1.
1559 : */
1560 30495 : *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
1561 30495 : p = (*buffer)->ptr;
1562 0 : } else if (!first_iteration) {
1563 : /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
1564 0 : if (!header.size) {
1565 0 : break;
1566 : }
1567 :
1568 : /*
1569 : We have to realloc the buffer.
1570 :
1571 : We need a trailing \0 for the last string, in case of text-mode,
1572 : to be able to implement read-only variables.
1573 : */
1574 0 : (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC);
1575 : /* The position could have changed, recalculate */
1576 0 : p = (*buffer)->ptr + (*data_size - header.size);
1577 : }
1578 :
1579 30495 : if ((ret = mysqlnd_read_body(conn, &header, p TSRMLS_CC))) {
1580 0 : DBG_ERR("Empty row packet body");
1581 0 : php_error(E_WARNING, "Empty row packet body");
1582 0 : break;
1583 : }
1584 :
1585 30495 : if (header.size < MYSQLND_MAX_PACKET_SIZE) {
1586 30495 : break;
1587 : }
1588 0 : }
1589 30496 : if (ret == FAIL && *buffer) {
1590 0 : (*buffer)->free_chunk((*buffer), TRUE TSRMLS_CC);
1591 0 : *buffer = NULL;
1592 : }
1593 30496 : *data_size -= prealloc_more_bytes;
1594 30496 : DBG_RETURN(ret);
1595 : }
1596 :
1597 :
1598 : /* {{{ php_mysqlnd_rowp_read_binary_protocol */
1599 : void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
1600 : unsigned int field_count, MYSQLND_FIELD *fields_metadata,
1601 : zend_bool persistent,
1602 : zend_bool as_unicode, zend_bool as_int_or_float,
1603 : MYSQLND_THD_ZVAL_PCACHE * zval_cache,
1604 : MYSQLND_STATS * stats TSRMLS_DC)
1605 5906 : {
1606 : int i;
1607 5906 : zend_uchar *p = row_buffer->ptr;
1608 : zend_uchar *null_ptr, bit;
1609 : zval **current_field, **end_field, **start_field;
1610 : #ifdef USE_ZVAL_CACHE
1611 : zend_bool allocated;
1612 : void *obj;
1613 : #endif
1614 :
1615 5906 : DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
1616 :
1617 5906 : end_field = (current_field = start_field = fields) + field_count;
1618 :
1619 :
1620 : /* skip the first byte, not 0xFE -> 0x0, status */
1621 5906 : p++;
1622 5906 : null_ptr= p;
1623 5906 : p += (field_count + 9)/8; /* skip null bits */
1624 5906 : bit = 4; /* first 2 bits are reserved */
1625 :
1626 21702 : for (i = 0; current_field < end_field; current_field++, i++) {
1627 : #ifdef USE_ZVAL_CACHE
1628 : DBG_INF("Trying to use the zval cache");
1629 : obj = mysqlnd_palloc_get_zval(zval_cache, &allocated TSRMLS_CC);
1630 : if (allocated) {
1631 : *current_field = (zval *) obj;
1632 : } else {
1633 : /* It's from the cache, so we can upcast here */
1634 : *current_field = &((mysqlnd_zval *) obj)->zv;
1635 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;
1636 : }
1637 : #else
1638 15796 : DBG_INF("Directly creating zval");
1639 15796 : MAKE_STD_ZVAL(*current_field);
1640 : #endif
1641 :
1642 15796 : DBG_INF_FMT("Into zval=%p decoding column %d [%s.%s.%s] type=%d field->flags&unsigned=%d flags=%u is_bit=%d as_unicode=%d",
1643 : *current_field, i,
1644 : fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
1645 : fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
1646 15796 : if (*null_ptr & bit) {
1647 1151 : DBG_INF("It's null");
1648 1151 : ZVAL_NULL(*current_field);
1649 1151 : MYSQLND_INC_CONN_STATISTIC(stats, STAT_BINARY_TYPE_FETCHED_NULL);
1650 : } else {
1651 14645 : enum_mysqlnd_field_types type = fields_metadata[i].type;
1652 14645 : mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i],
1653 : 0, &p, as_unicode TSRMLS_CC);
1654 :
1655 14645 : if (MYSQLND_G(collect_statistics)) {
1656 : enum_mysqlnd_collected_stats statistic;
1657 14645 : switch (fields_metadata[i].type) {
1658 0 : case MYSQL_TYPE_DECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
1659 57 : case MYSQL_TYPE_TINY: statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
1660 41 : case MYSQL_TYPE_SHORT: statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
1661 4880 : case MYSQL_TYPE_LONG: statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
1662 114 : case MYSQL_TYPE_FLOAT: statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
1663 50 : case MYSQL_TYPE_DOUBLE: statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
1664 0 : case MYSQL_TYPE_NULL: statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
1665 18 : case MYSQL_TYPE_TIMESTAMP: statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
1666 2049 : case MYSQL_TYPE_LONGLONG: statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
1667 26 : case MYSQL_TYPE_INT24: statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
1668 20 : case MYSQL_TYPE_DATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
1669 20 : case MYSQL_TYPE_TIME: statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
1670 20 : case MYSQL_TYPE_DATETIME: statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
1671 18 : case MYSQL_TYPE_YEAR: statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
1672 0 : case MYSQL_TYPE_NEWDATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
1673 0 : case MYSQL_TYPE_VARCHAR: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1674 1487 : case MYSQL_TYPE_BIT: statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
1675 50 : case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
1676 0 : case MYSQL_TYPE_ENUM: statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
1677 0 : case MYSQL_TYPE_SET: statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
1678 0 : case MYSQL_TYPE_TINY_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1679 0 : case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1680 0 : case MYSQL_TYPE_LONG_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1681 1147 : case MYSQL_TYPE_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
1682 4350 : case MYSQL_TYPE_VAR_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1683 260 : case MYSQL_TYPE_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
1684 38 : case MYSQL_TYPE_GEOMETRY: statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
1685 0 : default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
1686 : }
1687 14645 : MYSQLND_INC_CONN_STATISTIC(stats, statistic);
1688 : }
1689 : }
1690 15796 : if (!((bit<<=1) & 255)) {
1691 151 : bit = 1; /* to the following byte */
1692 151 : null_ptr++;
1693 : }
1694 : }
1695 :
1696 : DBG_VOID_RETURN;
1697 : }
1698 : /* }}} */
1699 :
1700 :
1701 : /* {{{ php_mysqlnd_rowp_read_text_protocol */
1702 : void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
1703 : unsigned int field_count, MYSQLND_FIELD *fields_metadata,
1704 : zend_bool persistent,
1705 : zend_bool as_unicode, zend_bool as_int_or_float,
1706 : MYSQLND_THD_ZVAL_PCACHE * zval_cache,
1707 : MYSQLND_STATS * stats TSRMLS_DC)
1708 17358 : {
1709 : int i;
1710 17358 : zend_bool last_field_was_string = FALSE;
1711 : zval **current_field, **end_field, **start_field;
1712 17358 : zend_uchar *p = row_buffer->ptr;
1713 17358 : size_t data_size = row_buffer->app;
1714 17358 : zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
1715 :
1716 17358 : DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
1717 :
1718 17358 : end_field = (current_field = start_field = fields) + field_count;
1719 55215 : for (i = 0; current_field < end_field; current_field++, i++) {
1720 : /* Don't reverse the order. It is significant!*/
1721 37857 : void *obj = NULL;
1722 37857 : zend_bool allocated = TRUE;
1723 37857 : zend_uchar *this_field_len_pos = p;
1724 : /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
1725 37857 : unsigned long len = php_mysqlnd_net_field_length(&p);
1726 :
1727 : #ifdef USE_ZVAL_CACHE
1728 : obj = mysqlnd_palloc_get_zval(zval_cache, &allocated TSRMLS_CC);
1729 : if (allocated) {
1730 : *current_field = (zval *) obj;
1731 : } else {
1732 : /* It's from the cache, so we can upcast here */
1733 : *current_field = &((mysqlnd_zval *) obj)->zv;
1734 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_FREE;
1735 : }
1736 : #else
1737 37857 : DBG_INF("Directly creating zval");
1738 37857 : MAKE_STD_ZVAL(*current_field);
1739 : #endif
1740 :
1741 37857 : if (current_field > start_field && last_field_was_string) {
1742 : /*
1743 : Normal queries:
1744 : We have to put \0 now to the end of the previous field, if it was
1745 : a string. IS_NULL doesn't matter. Because we have already read our
1746 : length, then we can overwrite it in the row buffer.
1747 : This statement terminates the previous field, not the current one.
1748 :
1749 : NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
1750 : Any string's length is encoded in at least one byte, so we can stick
1751 : a \0 there.
1752 : */
1753 :
1754 20442 : *this_field_len_pos = '\0';
1755 : }
1756 :
1757 : /* NULL or NOT NULL, this is the question! */
1758 37857 : if (len == MYSQLND_NULL_LENGTH) {
1759 739 : ZVAL_NULL(*current_field);
1760 739 : last_field_was_string = FALSE;
1761 : } else {
1762 : #if PHP_MAJOR_VERSION >= 6 || defined(MYSQLND_STRING_TO_INT_CONVERSION)
1763 : struct st_mysqlnd_perm_bind perm_bind =
1764 37118 : mysqlnd_ps_fetch_functions[fields_metadata[i].type];
1765 : #endif
1766 37118 : if (MYSQLND_G(collect_statistics)) {
1767 : enum_mysqlnd_collected_stats statistic;
1768 37094 : switch (fields_metadata[i].type) {
1769 0 : case MYSQL_TYPE_DECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1770 35 : case MYSQL_TYPE_TINY: statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
1771 38 : case MYSQL_TYPE_SHORT: statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
1772 16155 : case MYSQL_TYPE_LONG: statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
1773 35 : case MYSQL_TYPE_FLOAT: statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
1774 33 : case MYSQL_TYPE_DOUBLE: statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
1775 0 : case MYSQL_TYPE_NULL: statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
1776 1795 : case MYSQL_TYPE_TIMESTAMP: statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
1777 1151 : case MYSQL_TYPE_LONGLONG: statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
1778 25 : case MYSQL_TYPE_INT24: statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
1779 19 : case MYSQL_TYPE_DATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1780 19 : case MYSQL_TYPE_TIME: statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
1781 21 : case MYSQL_TYPE_DATETIME: statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
1782 16 : case MYSQL_TYPE_YEAR: statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
1783 0 : case MYSQL_TYPE_NEWDATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
1784 0 : case MYSQL_TYPE_VARCHAR: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1785 462 : case MYSQL_TYPE_BIT: statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
1786 35 : case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
1787 0 : case MYSQL_TYPE_ENUM: statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
1788 0 : case MYSQL_TYPE_SET: statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
1789 0 : case MYSQL_TYPE_TINY_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1790 7 : case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1791 0 : case MYSQL_TYPE_LONG_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1792 1926 : case MYSQL_TYPE_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
1793 3862 : case MYSQL_TYPE_VAR_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1794 11432 : case MYSQL_TYPE_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
1795 28 : case MYSQL_TYPE_GEOMETRY: statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
1796 0 : default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
1797 : }
1798 37094 : MYSQLND_INC_CONN_STATISTIC(stats, statistic);
1799 : }
1800 :
1801 : #ifdef MYSQLND_STRING_TO_INT_CONVERSION
1802 37118 : if (as_int_or_float && perm_bind.php_type == IS_LONG &&
1803 : perm_bind.pack_len <= SIZEOF_LONG)
1804 : {
1805 0 : zend_uchar save = *(p + len);
1806 : /* We have to make it ASCIIZ temporarily */
1807 0 : *(p + len) = '\0';
1808 0 : if (perm_bind.pack_len < SIZEOF_LONG)
1809 : {
1810 : /* direct conversion */
1811 : int64_t v =
1812 : #ifndef PHP_WIN32
1813 0 : atoll((char *) p);
1814 : #else
1815 : _atoi64((char *) p);
1816 : #endif
1817 0 : ZVAL_LONG(*current_field, v);
1818 : } else {
1819 : uint64_t v =
1820 : #ifndef PHP_WIN32
1821 0 : (uint64_t) atoll((char *) p);
1822 : #else
1823 : (uint64_t) _atoi64((char *) p);
1824 : #endif
1825 0 : zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
1826 : /* We have to make it ASCIIZ temporarily */
1827 : #if SIZEOF_LONG==8
1828 : if (uns == TRUE && v > 9223372036854775807L)
1829 : #elif SIZEOF_LONG==4
1830 0 : if ((uns == TRUE && v > L64(2147483647)) ||
1831 : (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
1832 : (L64(-2147483648) > (int64_t) v))))
1833 : #endif /* SIZEOF */
1834 : {
1835 0 : ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1836 : } else {
1837 0 : ZVAL_LONG(*current_field, (int64_t)v);
1838 : }
1839 : }
1840 0 : *(p + len) = save;
1841 37118 : } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
1842 0 : zend_uchar save = *(p + len);
1843 : /* We have to make it ASCIIZ temporarily */
1844 0 : *(p + len) = '\0';
1845 0 : ZVAL_DOUBLE(*current_field, atof((char *) p));
1846 0 : *(p + len) = save;
1847 : } else
1848 : #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
1849 37118 : if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
1850 : /*
1851 : BIT fields are specially handled. As they come as bit mask, we have
1852 : to convert it to human-readable representation. As the bits take
1853 : less space in the protocol than the numbers they represent, we don't
1854 : have enough space in the packet buffer to overwrite inside.
1855 : Thus, a bit more space is pre-allocated at the end of the buffer,
1856 : see php_mysqlnd_rowp_read(). And we add the strings at the end.
1857 : Definitely not nice, _hackish_ :(, but works.
1858 : */
1859 462 : zend_uchar *start = bit_area;
1860 462 : ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]),
1861 : 0, &p, as_unicode, len TSRMLS_CC);
1862 : /*
1863 : We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
1864 : later in this function there will be an advancement.
1865 : */
1866 462 : p -= len;
1867 462 : if (Z_TYPE_PP(current_field) == IS_LONG) {
1868 339 : bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
1869 : #if PHP_MAJOR_VERSION >= 6
1870 339 : if (as_unicode) {
1871 339 : ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
1872 : } else
1873 : #endif
1874 : {
1875 0 : ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
1876 : }
1877 339 : if (allocated == FALSE) {
1878 0 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
1879 : }
1880 123 : } else if (Z_TYPE_PP(current_field) == IS_STRING){
1881 0 : memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
1882 0 : bit_area += Z_STRLEN_PP(current_field);
1883 0 : *bit_area++ = '\0';
1884 0 : zval_dtor(*current_field);
1885 : #if PHP_MAJOR_VERSION >= 6
1886 0 : if (as_unicode) {
1887 0 : ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
1888 : } else
1889 : #endif
1890 : {
1891 0 : ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
1892 : }
1893 0 : if (allocated == FALSE) {
1894 0 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
1895 : }
1896 : }
1897 : /*
1898 : IS_UNICODE should not be specially handled. In unicode mode
1899 : the buffers are not referenced - everything is copied.
1900 : */
1901 : } else
1902 : #if PHP_MAJOR_VERSION < 6
1903 : {
1904 : ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1905 : if (allocated == FALSE) {
1906 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
1907 : }
1908 : }
1909 : #else
1910 : /*
1911 : Here we have to convert to UTF16, which means not reusing the buffer.
1912 : Which in turn means that we can free the buffers once we have
1913 : stored the result set, if we use store_result().
1914 :
1915 : Also the destruction of the zvals should not call zval_copy_ctor()
1916 : because then we will leak.
1917 :
1918 : I suppose we can use UG(unicode) in mysqlnd.c/mysqlnd_palloc.c when
1919 : freeing a result set
1920 : to check if we need to call copy_ctor().
1921 :
1922 : XXX: Keep in mind that up there there is an open `else` in
1923 : #ifdef MYSQLND_STRING_TO_INT_CONVERSION
1924 : which will make with this `if` an `else if`.
1925 : */
1926 36773 : if ((perm_bind.is_possibly_blob == TRUE &&
1927 : fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
1928 : (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
1929 : {
1930 : /* BLOB - no conversion please */
1931 117 : ZVAL_STRINGL(*current_field, (char *)p, len, 0);
1932 : } else {
1933 36539 : ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
1934 : }
1935 37118 : if (allocated == FALSE) {
1936 : /*
1937 : The zval cache will check and see that the type is IS_STRING.
1938 : In this case it will call copy_ctor(). This is valid when
1939 : allocated == TRUE . In this case we can't upcast. Thus for non-PS
1940 : point_type doesn't matter much, as the valuable information is
1941 : in the type of result set. Still good to set it.
1942 : */
1943 0 : if (Z_TYPE_P(*current_field) == IS_STRING) {
1944 0 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
1945 : } else {
1946 0 : ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;
1947 : }
1948 : }
1949 : #endif
1950 37118 : p += len;
1951 37118 : last_field_was_string = TRUE;
1952 : }
1953 : }
1954 17358 : if (last_field_was_string) {
1955 : /* Normal queries: The buffer has one more byte at the end, because we need it */
1956 16676 : row_buffer->ptr[data_size] = '\0';
1957 : }
1958 :
1959 : DBG_VOID_RETURN;
1960 : }
1961 : /* }}} */
1962 :
1963 :
1964 : /* {{{ php_mysqlnd_rowp_read */
1965 : /*
1966 : if normal statements => packet->fields is created by this function,
1967 : if PS => packet->fields is passed from outside
1968 : */
1969 : static enum_func_status
1970 : php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
1971 30496 : {
1972 30496 : MYSQLND_NET *net = &conn->net;
1973 : zend_uchar *p;
1974 30496 : enum_func_status ret = PASS;
1975 30496 : size_t old_chunk_size = net->stream->chunk_size;
1976 30496 : php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet;
1977 30496 : size_t post_alloc_for_bit_fields = 0;
1978 30496 : uint64_t data_size = 0;
1979 :
1980 30496 : DBG_ENTER("php_mysqlnd_rowp_read");
1981 :
1982 30496 : if (!packet->binary_protocol && packet->bit_fields_count) {
1983 : /* For every field we need terminating \0 */
1984 965 : post_alloc_for_bit_fields =
1985 : packet->bit_fields_total_len + packet->bit_fields_count;
1986 : }
1987 :
1988 30496 : ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
1989 : packet->persistent_alloc, post_alloc_for_bit_fields
1990 : TSRMLS_CC);
1991 30496 : if (FAIL == ret) {
1992 1 : goto end;
1993 : }
1994 30495 : MYSQLND_INC_CONN_STATISTIC_W_VALUE2(&conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
1995 : MYSQLND_HEADER_SIZE + packet->header.size,
1996 : packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
1997 : 1);
1998 :
1999 : /* packet->row_buffer->ptr is of size 'data_size + 1' */
2000 30495 : packet->header.size = data_size;
2001 30495 : packet->row_buffer->app = data_size;
2002 :
2003 30495 : if ((*(p = packet->row_buffer->ptr)) == 0xFF) {
2004 : /*
2005 : Error message as part of the result set,
2006 : not good but we should not hang. See:
2007 : Bug #27876 : SF with cyrillic variable name fails during execution
2008 : */
2009 0 : ret = FAIL;
2010 0 : php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
2011 : packet->error_info.error,
2012 : sizeof(packet->error_info.error),
2013 : &packet->error_info.error_no,
2014 : packet->error_info.sqlstate
2015 : TSRMLS_CC);
2016 36547 : } else if (*p == 0xFE && data_size < 8) { /* EOF */
2017 6052 : packet->eof = TRUE;
2018 6052 : p++;
2019 6052 : if (data_size > 1) {
2020 6052 : packet->warning_count = uint2korr(p);
2021 6052 : p += 2;
2022 6052 : packet->server_status = uint2korr(p);
2023 : /* Seems we have 3 bytes reserved for future use */
2024 6052 : DBG_INF_FMT("server_status=%d warning_count=%d",
2025 : packet->server_status, packet->warning_count);
2026 : }
2027 : } else {
2028 24443 : MYSQLND_INC_CONN_STATISTIC(&conn->stats,
2029 : packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
2030 : STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
2031 :
2032 24443 : packet->eof = FALSE;
2033 : /* packet->field_count is set by the user of the packet */
2034 :
2035 24443 : if (!packet->skip_extraction) {
2036 3613 : if (!packet->fields) {
2037 3613 : DBG_INF("Allocating packet->fields");
2038 : /*
2039 : old-API will probably set packet->fields to NULL every time, though for
2040 : unbuffered sets it makes not much sense as the zvals in this buffer matter,
2041 : not the buffer. Constantly allocating and deallocating brings nothing.
2042 :
2043 : For PS - if stmt_store() is performed, thus we don't have a cursor, it will
2044 : behave just like old-API buffered. Cursors will behave like a bit different,
2045 : but mostly like old-API unbuffered and thus will populate this array with
2046 : value.
2047 : */
2048 3613 : packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
2049 : packet->persistent_alloc);
2050 : }
2051 : } else {
2052 20830 : MYSQLND_INC_CONN_STATISTIC(&conn->stats,
2053 : packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
2054 : STAT_ROWS_SKIPPED_NORMAL);
2055 : }
2056 : }
2057 :
2058 30496 : end:
2059 30496 : net->stream->chunk_size = old_chunk_size;
2060 30496 : DBG_RETURN(ret);
2061 : }
2062 : /* }}} */
2063 :
2064 :
2065 : /* {{{ php_mysqlnd_rowp_free_mem */
2066 : static
2067 : void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
2068 6036 : {
2069 : php_mysql_packet_row *p;
2070 :
2071 6036 : DBG_ENTER("php_mysqlnd_rowp_free_mem");
2072 6036 : p = (php_mysql_packet_row *) _packet;
2073 6036 : if (p->row_buffer) {
2074 6028 : p->row_buffer->free_chunk(p->row_buffer, TRUE TSRMLS_CC);
2075 6028 : p->row_buffer = NULL;
2076 : }
2077 6036 : DBG_INF_FMT("alloca=%d persistent=%d", (int)alloca, (int)p->header.persistent);
2078 : /*
2079 : Don't free packet->fields :
2080 : - normal queries -> store_result() | fetch_row_unbuffered() will transfer
2081 : the ownership and NULL it.
2082 : - PS will pass in it the bound variables, we have to use them! and of course
2083 : not free the array. As it is passed to us, we should not clean it ourselves.
2084 : */
2085 6036 : if (!alloca) {
2086 6036 : mnd_pefree(p, p->header.persistent);
2087 : }
2088 : DBG_VOID_RETURN;
2089 : }
2090 : /* }}} */
2091 :
2092 :
2093 :
2094 : /* {{{ php_mysqlnd_stats_read */
2095 : static enum_func_status
2096 : php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)
2097 7 : {
2098 7 : php_mysql_packet_stats *packet= (php_mysql_packet_stats *) _packet;
2099 7 : size_t buf_len = conn->net.cmd_buffer.length;
2100 7 : zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
2101 :
2102 7 : DBG_ENTER("php_mysqlnd_stats_read");
2103 :
2104 7 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
2105 :
2106 7 : packet->message = mnd_emalloc(packet->header.size + 1);
2107 7 : memcpy(packet->message, buf, packet->header.size);
2108 7 : packet->message[packet->header.size] = '\0';
2109 7 : packet->message_len = packet->header.size;
2110 :
2111 7 : DBG_RETURN(PASS);
2112 : }
2113 : /* }}} */
2114 :
2115 :
2116 : /* {{{ php_mysqlnd_stats_free_mem */
2117 : static
2118 : void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
2119 7 : {
2120 7 : php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet;
2121 7 : if (p->message) {
2122 0 : mnd_efree(p->message);
2123 0 : p->message = NULL;
2124 : }
2125 7 : if (!alloca) {
2126 0 : mnd_pefree(p, p->header.persistent);
2127 : }
2128 7 : }
2129 : /* }}} */
2130 :
2131 :
2132 : /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
2133 : #define PREPARE_RESPONSE_SIZE_41 9
2134 : #define PREPARE_RESPONSE_SIZE_50 12
2135 :
2136 : /* {{{ php_mysqlnd_prepare_read */
2137 : static enum_func_status
2138 : php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC)
2139 4133 : {
2140 : /* In case of an error, we should have place to put it */
2141 4133 : size_t buf_len = conn->net.cmd_buffer.length;
2142 4133 : zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
2143 4133 : zend_uchar *p = buf;
2144 4133 : zend_uchar *begin = buf;
2145 : unsigned int data_size;
2146 4133 : php_mysql_packet_prepare_response *packet= (php_mysql_packet_prepare_response *) _packet;
2147 :
2148 4133 : DBG_ENTER("php_mysqlnd_prepare_read");
2149 :
2150 4133 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
2151 :
2152 4132 : data_size = packet->header.size;
2153 4132 : packet->error_code = uint1korr(p);
2154 4132 : p++;
2155 :
2156 4132 : if (0xFF == packet->error_code) {
2157 26 : php_mysqlnd_read_error_from_line(p, data_size - 1,
2158 : packet->error_info.error,
2159 : sizeof(packet->error_info.error),
2160 : &packet->error_info.error_no,
2161 : packet->error_info.sqlstate
2162 : TSRMLS_CC);
2163 26 : DBG_RETURN(PASS);
2164 : }
2165 :
2166 4106 : if (data_size != PREPARE_RESPONSE_SIZE_41 &&
2167 : data_size != PREPARE_RESPONSE_SIZE_50 &&
2168 : !(data_size > PREPARE_RESPONSE_SIZE_50)) {
2169 0 : DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %d", data_size);
2170 0 : php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d", data_size);
2171 0 : DBG_RETURN(FAIL);
2172 : }
2173 :
2174 4106 : packet->stmt_id = uint4korr(p);
2175 4106 : p += 4;
2176 :
2177 : /* Number of columns in result set */
2178 4106 : packet->field_count = uint2korr(p);
2179 4106 : p += 2;
2180 :
2181 4106 : packet->param_count = uint2korr(p);
2182 4106 : p += 2;
2183 :
2184 4106 : if (data_size > 9) {
2185 : /* 0x0 filler sent by the server for 5.0+ clients */
2186 4106 : p++;
2187 :
2188 4106 : packet->warning_count = uint2korr(p);
2189 : }
2190 :
2191 4106 : DBG_INF_FMT("Prepare packet read: stmt_id=%d fields=%d params=%d",
2192 : packet->stmt_id, packet->field_count, packet->param_count);
2193 :
2194 4106 : if (p - begin > packet->header.size) {
2195 0 : DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
2196 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
2197 : p - begin - packet->header.size);
2198 : }
2199 :
2200 4106 : DBG_RETURN(PASS);
2201 : }
2202 : /* }}} */
2203 :
2204 :
2205 : /* {{{ php_mysqlnd_prepare_free_mem */
2206 : static
2207 : void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
2208 4106 : {
2209 4106 : php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet;
2210 4106 : if (!alloca) {
2211 0 : mnd_pefree(p, p->header.persistent);
2212 : }
2213 4106 : }
2214 : /* }}} */
2215 :
2216 :
2217 : /* {{{ php_mysqlnd_chg_user_read */
2218 : static enum_func_status
2219 : php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC)
2220 63 : {
2221 : /* There could be an error message */
2222 63 : size_t buf_len = conn->net.cmd_buffer.length;
2223 63 : zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
2224 63 : zend_uchar *p = buf;
2225 63 : zend_uchar *begin = buf;
2226 63 : php_mysql_packet_chg_user_resp *packet= (php_mysql_packet_chg_user_resp *) _packet;
2227 :
2228 63 : DBG_ENTER("php_mysqlnd_chg_user_read");
2229 :
2230 63 : PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_PACKET);
2231 :
2232 : /*
2233 : Don't increment. First byte is 0xFF on error, but otherwise is starting byte
2234 : of encoded sequence for length.
2235 : */
2236 :
2237 : /* Should be always 0x0 or 0xFF for error */
2238 63 : packet->field_count= uint1korr(p);
2239 63 : p++;
2240 :
2241 63 : if (packet->header.size == 1 && buf[0] == 0xFE &&
2242 : packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
2243 : /* We don't handle 3.23 authentication */
2244 0 : DBG_RETURN(FAIL);
2245 : }
2246 :
2247 63 : if (0xFF == packet->field_count) {
2248 11 : php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
2249 : packet->error_info.error,
2250 : sizeof(packet->error_info.error),
2251 : &packet->error_info.error_no,
2252 : packet->error_info.sqlstate
2253 : TSRMLS_CC);
2254 : }
2255 63 : if (p - begin > packet->header.size) {
2256 0 : DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
2257 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
2258 : p - begin - packet->header.size);
2259 : }
2260 :
2261 63 : DBG_RETURN(PASS);
2262 : }
2263 : /* }}} */
2264 :
2265 :
2266 : /* {{{ php_mysqlnd_chg_user_free_mem */
2267 : static
2268 : void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
2269 63 : {
2270 63 : if (!alloca) {
2271 0 : mnd_pefree(_packet, ((php_mysql_packet_chg_user_resp *)_packet)->header.persistent);
2272 : }
2273 63 : }
2274 : /* }}} */
2275 :
2276 :
2277 : /* {{{ packet_methods
2278 : */
2279 : mysqlnd_packet_methods packet_methods[PROT_LAST] =
2280 : {
2281 : {
2282 : sizeof(php_mysql_packet_greet),
2283 : php_mysqlnd_greet_read,
2284 : NULL, /* write */
2285 : php_mysqlnd_greet_free_mem,
2286 : }, /* PROT_GREET_PACKET */
2287 : {
2288 : sizeof(php_mysql_packet_auth),
2289 : NULL, /* read */
2290 : php_mysqlnd_auth_write,
2291 : php_mysqlnd_auth_free_mem,
2292 : }, /* PROT_AUTH_PACKET */
2293 : {
2294 : sizeof(php_mysql_packet_ok),
2295 : php_mysqlnd_ok_read, /* read */
2296 : NULL, /* write */
2297 : php_mysqlnd_ok_free_mem,
2298 : }, /* PROT_OK_PACKET */
2299 : {
2300 : sizeof(php_mysql_packet_eof),
2301 : php_mysqlnd_eof_read, /* read */
2302 : NULL, /* write */
2303 : php_mysqlnd_eof_free_mem,
2304 : }, /* PROT_EOF_PACKET */
2305 : {
2306 : sizeof(php_mysql_packet_command),
2307 : NULL, /* read */
2308 : php_mysqlnd_cmd_write, /* write */
2309 : php_mysqlnd_cmd_free_mem,
2310 : }, /* PROT_CMD_PACKET */
2311 : {
2312 : sizeof(php_mysql_packet_rset_header),
2313 : php_mysqlnd_rset_header_read, /* read */
2314 : NULL, /* write */
2315 : php_mysqlnd_rset_header_free_mem,
2316 : }, /* PROT_RSET_HEADER_PACKET */
2317 : {
2318 : sizeof(php_mysql_packet_res_field),
2319 : php_mysqlnd_rset_field_read, /* read */
2320 : NULL, /* write */
2321 : php_mysqlnd_rset_field_free_mem,
2322 : }, /* PROT_RSET_FLD_PACKET */
2323 : {
2324 : sizeof(php_mysql_packet_row),
2325 : php_mysqlnd_rowp_read, /* read */
2326 : NULL, /* write */
2327 : php_mysqlnd_rowp_free_mem,
2328 : }, /* PROT_ROW_PACKET */
2329 : {
2330 : sizeof(php_mysql_packet_stats),
2331 : php_mysqlnd_stats_read, /* read */
2332 : NULL, /* write */
2333 : php_mysqlnd_stats_free_mem,
2334 : }, /* PROT_STATS_PACKET */
2335 : {
2336 : sizeof(php_mysql_packet_prepare_response),
2337 : php_mysqlnd_prepare_read, /* read */
2338 : NULL, /* write */
2339 : php_mysqlnd_prepare_free_mem,
2340 : }, /* PROT_PREPARE_RESP_PACKET */
2341 : {
2342 : sizeof(php_mysql_packet_chg_user_resp),
2343 : php_mysqlnd_chg_user_read, /* read */
2344 : NULL, /* write */
2345 : php_mysqlnd_chg_user_free_mem,
2346 : } /* PROT_CHG_USER_PACKET */
2347 : };
2348 : /* }}} */
2349 :
2350 :
2351 : /*
2352 : * Local variables:
2353 : * tab-width: 4
2354 : * c-basic-offset: 4
2355 : * End:
2356 : * vim600: noet sw=4 ts=4 fdm=marker
2357 : * vim<600: noet sw=4 ts=4
2358 : */
|