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