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