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