PageRenderTime 56ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/mysqlnd/mysqlnd_wireprotocol.c

http://github.com/infusion/PHP
C | 2158 lines | 1551 code | 294 blank | 313 comment | 235 complexity | 94503b78a3dfd171fb2d4ea05b3d6435 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2006-2011 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Georg Richter <georg@mysql.com> |
  16. | Andrey Hristov <andrey@mysql.com> |
  17. | Ulf Wendel <uwendel@mysql.com> |
  18. +----------------------------------------------------------------------+
  19. */
  20. #include "php.h"
  21. #include "php_globals.h"
  22. #include "mysqlnd.h"
  23. #include "mysqlnd_priv.h"
  24. #include "mysqlnd_wireprotocol.h"
  25. #include "mysqlnd_statistics.h"
  26. #include "mysqlnd_debug.h"
  27. #include "mysqlnd_block_alloc.h"
  28. #include "ext/standard/sha1.h"
  29. #include "zend_ini.h"
  30. #define MYSQLND_SILENT 1
  31. #define MYSQLND_DUMP_HEADER_N_BODY
  32. #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
  33. { \
  34. DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
  35. if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
  36. CONN_SET_STATE(conn, CONN_QUIT_SENT); \
  37. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
  38. php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
  39. DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
  40. DBG_RETURN(FAIL);\
  41. }\
  42. if ((buf_size) < (packet)->header.size) { \
  43. DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
  44. (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
  45. DBG_RETURN(FAIL); \
  46. }\
  47. if (FAIL == conn->net->m.receive((conn), (buf), (packet)->header.size TSRMLS_CC)) { \
  48. CONN_SET_STATE(conn, CONN_QUIT_SENT); \
  49. SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
  50. php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
  51. DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
  52. DBG_RETURN(FAIL);\
  53. } \
  54. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
  55. MYSQLND_HEADER_SIZE + (packet)->header.size, \
  56. packet_type_to_statistic_packet_count[packet_type], \
  57. 1); \
  58. }
  59. #define BAIL_IF_NO_MORE_DATA \
  60. if ((size_t)(p - begin) > packet->header.size) { \
  61. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \
  62. goto premature_end; \
  63. } \
  64. static const char *unknown_sqlstate= "HY000";
  65. const char * const mysqlnd_empty_string = "";
  66. /* Used in mysqlnd_debug.c */
  67. const char mysqlnd_read_header_name[] = "mysqlnd_read_header";
  68. const char mysqlnd_read_body_name[] = "mysqlnd_read_body";
  69. #define ERROR_MARKER 0xFF
  70. #define EODATA_MARKER 0xFE
  71. /* {{{ mysqlnd_command_to_text
  72. */
  73. const char * const mysqlnd_command_to_text[COM_END] =
  74. {
  75. "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
  76. "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
  77. "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
  78. "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
  79. "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
  80. "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
  81. "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
  82. };
  83. /* }}} */
  84. static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
  85. {
  86. STAT_LAST,
  87. STAT_LAST,
  88. STAT_BYTES_RECEIVED_OK,
  89. STAT_BYTES_RECEIVED_EOF,
  90. STAT_LAST,
  91. STAT_BYTES_RECEIVED_RSET_HEADER,
  92. STAT_BYTES_RECEIVED_RSET_FIELD_META,
  93. STAT_BYTES_RECEIVED_RSET_ROW,
  94. STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
  95. STAT_BYTES_RECEIVED_CHANGE_USER,
  96. };
  97. static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
  98. {
  99. STAT_LAST,
  100. STAT_LAST,
  101. STAT_PACKETS_RECEIVED_OK,
  102. STAT_PACKETS_RECEIVED_EOF,
  103. STAT_LAST,
  104. STAT_PACKETS_RECEIVED_RSET_HEADER,
  105. STAT_PACKETS_RECEIVED_RSET_FIELD_META,
  106. STAT_PACKETS_RECEIVED_RSET_ROW,
  107. STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
  108. STAT_PACKETS_RECEIVED_CHANGE_USER,
  109. };
  110. /* {{{ php_mysqlnd_net_field_length
  111. Get next field's length */
  112. unsigned long
  113. php_mysqlnd_net_field_length(zend_uchar **packet)
  114. {
  115. register zend_uchar *p= (zend_uchar *)*packet;
  116. if (*p < 251) {
  117. (*packet)++;
  118. return (unsigned long) *p;
  119. }
  120. switch (*p) {
  121. case 251:
  122. (*packet)++;
  123. return MYSQLND_NULL_LENGTH;
  124. case 252:
  125. (*packet) += 3;
  126. return (unsigned long) uint2korr(p+1);
  127. case 253:
  128. (*packet) += 4;
  129. return (unsigned long) uint3korr(p+1);
  130. default:
  131. (*packet) += 9;
  132. return (unsigned long) uint4korr(p+1);
  133. }
  134. }
  135. /* }}} */
  136. /* {{{ php_mysqlnd_net_field_length_ll
  137. Get next field's length */
  138. uint64_t
  139. php_mysqlnd_net_field_length_ll(zend_uchar **packet)
  140. {
  141. register zend_uchar *p= (zend_uchar *)*packet;
  142. if (*p < 251) {
  143. (*packet)++;
  144. return (uint64_t) *p;
  145. }
  146. switch (*p) {
  147. case 251:
  148. (*packet)++;
  149. return (uint64_t) MYSQLND_NULL_LENGTH;
  150. case 252:
  151. (*packet) += 3;
  152. return (uint64_t) uint2korr(p + 1);
  153. case 253:
  154. (*packet) += 4;
  155. return (uint64_t) uint3korr(p + 1);
  156. default:
  157. (*packet) += 9;
  158. return (uint64_t) uint8korr(p + 1);
  159. }
  160. }
  161. /* }}} */
  162. /* {{{ php_mysqlnd_net_store_length */
  163. zend_uchar *
  164. php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
  165. {
  166. if (length < (uint64_t) L64(251)) {
  167. *packet = (zend_uchar) length;
  168. return packet + 1;
  169. }
  170. if (length < (uint64_t) L64(65536)) {
  171. *packet++ = 252;
  172. int2store(packet,(unsigned int) length);
  173. return packet + 2;
  174. }
  175. if (length < (uint64_t) L64(16777216)) {
  176. *packet++ = 253;
  177. int3store(packet,(ulong) length);
  178. return packet + 3;
  179. }
  180. *packet++ = 254;
  181. int8store(packet, length);
  182. return packet + 8;
  183. }
  184. /* }}} */
  185. /* {{{ php_mysqlnd_read_error_from_line */
  186. static enum_func_status
  187. php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
  188. char *error, int error_buf_len,
  189. unsigned int *error_no, char *sqlstate TSRMLS_DC)
  190. {
  191. zend_uchar *p = buf;
  192. int error_msg_len= 0;
  193. DBG_ENTER("php_mysqlnd_read_error_from_line");
  194. *error_no = CR_UNKNOWN_ERROR;
  195. memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
  196. if (buf_len > 2) {
  197. *error_no = uint2korr(p);
  198. p+= 2;
  199. /*
  200. sqlstate is following. No need to check for buf_left_len as we checked > 2 above,
  201. if it was >=2 then we would need a check
  202. */
  203. if (*p == '#') {
  204. ++p;
  205. if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) {
  206. memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH);
  207. p+= MYSQLND_SQLSTATE_LENGTH;
  208. } else {
  209. goto end;
  210. }
  211. }
  212. if ((buf_len - (p - buf)) > 0) {
  213. error_msg_len = MIN((int)((buf_len - (p - buf))), (int) (error_buf_len - 1));
  214. memcpy(error, p, error_msg_len);
  215. }
  216. }
  217. end:
  218. sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
  219. error[error_msg_len]= '\0';
  220. DBG_RETURN(FAIL);
  221. }
  222. /* }}} */
  223. /* {{{ mysqlnd_read_header */
  224. static enum_func_status
  225. mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC)
  226. {
  227. MYSQLND_NET * net = conn->net;
  228. zend_uchar buffer[MYSQLND_HEADER_SIZE];
  229. DBG_ENTER("mysqlnd_read_header_name");
  230. DBG_INF_FMT("compressed=%u conn_id=%u", net->compressed, conn->thread_id);
  231. if (FAIL == net->m.receive(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
  232. DBG_RETURN(FAIL);
  233. }
  234. header->size = uint3korr(buffer);
  235. header->packet_no = uint1korr(buffer + 3);
  236. #ifdef MYSQLND_DUMP_HEADER_N_BODY
  237. DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
  238. #endif
  239. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats,
  240. STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
  241. STAT_PACKETS_RECEIVED, 1);
  242. if (net->compressed || net->packet_no == header->packet_no) {
  243. /*
  244. Have to increase the number, so we can send correct number back. It will
  245. round at 255 as this is unsigned char. The server needs this for simple
  246. flow control checking.
  247. */
  248. net->packet_no++;
  249. DBG_RETURN(PASS);
  250. }
  251. DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
  252. net->packet_no, header->packet_no, header->size);
  253. php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
  254. net->packet_no, header->packet_no, header->size);
  255. DBG_RETURN(FAIL);
  256. }
  257. /* }}} */
  258. /* {{{ php_mysqlnd_greet_read */
  259. static enum_func_status
  260. php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
  261. {
  262. zend_uchar buf[2048];
  263. zend_uchar *p = buf;
  264. zend_uchar *begin = buf;
  265. MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
  266. DBG_ENTER("php_mysqlnd_greet_read");
  267. PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
  268. BAIL_IF_NO_MORE_DATA;
  269. packet->protocol_version = uint1korr(p);
  270. p++;
  271. BAIL_IF_NO_MORE_DATA;
  272. if (ERROR_MARKER == packet->protocol_version) {
  273. php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
  274. packet->error, sizeof(packet->error),
  275. &packet->error_no, packet->sqlstate
  276. TSRMLS_CC);
  277. /*
  278. The server doesn't send sqlstate in the greet packet.
  279. It's a bug#26426 , so we have to set it correctly ourselves.
  280. It's probably "Too many connections, which has SQL state 08004".
  281. */
  282. if (packet->error_no == 1040) {
  283. memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
  284. }
  285. DBG_RETURN(PASS);
  286. }
  287. packet->server_version = estrdup((char *)p);
  288. p+= strlen(packet->server_version) + 1; /* eat the '\0' */
  289. BAIL_IF_NO_MORE_DATA;
  290. packet->thread_id = uint4korr(p);
  291. p+=4;
  292. BAIL_IF_NO_MORE_DATA;
  293. memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
  294. p+= 8;
  295. BAIL_IF_NO_MORE_DATA;
  296. /* pad1 */
  297. p++;
  298. BAIL_IF_NO_MORE_DATA;
  299. packet->server_capabilities = uint2korr(p);
  300. p+= 2;
  301. BAIL_IF_NO_MORE_DATA;
  302. packet->charset_no = uint1korr(p);
  303. p++;
  304. BAIL_IF_NO_MORE_DATA;
  305. packet->server_status = uint2korr(p);
  306. p+= 2;
  307. BAIL_IF_NO_MORE_DATA;
  308. /* pad2 */
  309. p+= 13;
  310. BAIL_IF_NO_MORE_DATA;
  311. if ((size_t) (p - buf) < packet->header.size) {
  312. /* scramble_buf is split into two parts */
  313. memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
  314. p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
  315. } else {
  316. packet->pre41 = TRUE;
  317. }
  318. DBG_INF_FMT("proto=%u server=%s thread_id=%u",
  319. packet->protocol_version, packet->server_version, packet->thread_id);
  320. DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i",
  321. packet->server_capabilities, packet->charset_no, packet->server_status);
  322. DBG_RETURN(PASS);
  323. premature_end:
  324. DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
  325. php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
  326. p - begin - packet->header.size);
  327. DBG_RETURN(FAIL);
  328. }
  329. /* }}} */
  330. /* {{{ php_mysqlnd_greet_free_mem */
  331. static
  332. void php_mysqlnd_greet_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  333. {
  334. MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet;
  335. if (p->server_version) {
  336. efree(p->server_version);
  337. p->server_version = NULL;
  338. }
  339. if (!stack_allocation) {
  340. mnd_pefree(p, p->header.persistent);
  341. }
  342. }
  343. /* }}} */
  344. /* {{{ php_mysqlnd_crypt */
  345. static void
  346. php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
  347. {
  348. const zend_uchar *s1_end = s1 + len;
  349. while (s1 < s1_end) {
  350. *buffer++= *s1++ ^ *s2++;
  351. }
  352. }
  353. /* }}} */
  354. /* {{{ php_mysqlnd_scramble */
  355. void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password)
  356. {
  357. PHP_SHA1_CTX context;
  358. zend_uchar sha1[SHA1_MAX_LENGTH];
  359. zend_uchar sha2[SHA1_MAX_LENGTH];
  360. /* Phase 1: hash password */
  361. PHP_SHA1Init(&context);
  362. PHP_SHA1Update(&context, password, strlen((char *)password));
  363. PHP_SHA1Final(sha1, &context);
  364. /* Phase 2: hash sha1 */
  365. PHP_SHA1Init(&context);
  366. PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
  367. PHP_SHA1Final(sha2, &context);
  368. /* Phase 3: hash scramble + sha2 */
  369. PHP_SHA1Init(&context);
  370. PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
  371. PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
  372. PHP_SHA1Final(buffer, &context);
  373. /* let's crypt buffer now */
  374. php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
  375. }
  376. /* }}} */
  377. #define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SHA1_MAX_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 128)
  378. /* {{{ php_mysqlnd_auth_write */
  379. static
  380. size_t php_mysqlnd_auth_write(void *_packet, MYSQLND * conn TSRMLS_DC)
  381. {
  382. char buffer[AUTH_WRITE_BUFFER_LEN];
  383. register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
  384. int len;
  385. register MYSQLND_PACKET_AUTH *packet= (MYSQLND_PACKET_AUTH *) _packet;
  386. DBG_ENTER("php_mysqlnd_auth_write");
  387. int4store(p, packet->client_flags);
  388. p+= 4;
  389. int4store(p, packet->max_packet_size);
  390. p+= 4;
  391. int1store(p, packet->charset_no);
  392. p++;
  393. memset(p, 0, 23); /* filler */
  394. p+= 23;
  395. if (!packet->send_half_packet) {
  396. len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN);
  397. memcpy(p, packet->user, len);
  398. p+= len;
  399. *p++ = '\0';
  400. /* copy scrambled pass*/
  401. if (packet->password && packet->password[0]) {
  402. /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
  403. int1store(p, SHA1_MAX_LENGTH);
  404. p++;
  405. php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password);
  406. p+= SHA1_MAX_LENGTH;
  407. } else {
  408. /* Zero length */
  409. int1store(p, 0);
  410. p++;
  411. }
  412. if (packet->db) {
  413. size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len);
  414. memcpy(p, packet->db, real_db_len);
  415. p+= real_db_len;
  416. *p++= '\0';
  417. }
  418. /* Handle CLIENT_CONNECT_WITH_DB */
  419. /* no \0 for no DB */
  420. }
  421. DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
  422. }
  423. /* }}} */
  424. /* {{{ php_mysqlnd_auth_free_mem */
  425. static
  426. void php_mysqlnd_auth_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  427. {
  428. if (!stack_allocation) {
  429. MYSQLND_PACKET_AUTH * p = (MYSQLND_PACKET_AUTH *) _packet;
  430. mnd_pefree(p, p->header.persistent);
  431. }
  432. }
  433. /* }}} */
  434. #define OK_BUFFER_SIZE 2048
  435. /* {{{ php_mysqlnd_ok_read */
  436. static enum_func_status
  437. php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
  438. {
  439. zend_uchar local_buf[OK_BUFFER_SIZE];
  440. size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
  441. zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
  442. zend_uchar *p = buf;
  443. zend_uchar *begin = buf;
  444. unsigned long i;
  445. register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
  446. DBG_ENTER("php_mysqlnd_ok_read");
  447. PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
  448. BAIL_IF_NO_MORE_DATA;
  449. /* Should be always 0x0 or ERROR_MARKER for error */
  450. packet->field_count = uint1korr(p);
  451. p++;
  452. BAIL_IF_NO_MORE_DATA;
  453. if (ERROR_MARKER == packet->field_count) {
  454. php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
  455. packet->error, sizeof(packet->error),
  456. &packet->error_no, packet->sqlstate
  457. TSRMLS_CC);
  458. DBG_RETURN(PASS);
  459. }
  460. /* Everything was fine! */
  461. packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
  462. BAIL_IF_NO_MORE_DATA;
  463. packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
  464. BAIL_IF_NO_MORE_DATA;
  465. packet->server_status = uint2korr(p);
  466. p+= 2;
  467. BAIL_IF_NO_MORE_DATA;
  468. packet->warning_count = uint2korr(p);
  469. p+= 2;
  470. BAIL_IF_NO_MORE_DATA;
  471. /* There is a message */
  472. if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
  473. packet->message_len = MIN(i, buf_len - (p - begin));
  474. packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
  475. } else {
  476. packet->message = NULL;
  477. packet->message_len = 0;
  478. }
  479. DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u",
  480. packet->affected_rows, packet->last_insert_id, packet->server_status,
  481. packet->warning_count);
  482. BAIL_IF_NO_MORE_DATA;
  483. DBG_RETURN(PASS);
  484. premature_end:
  485. DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
  486. php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
  487. p - begin - packet->header.size);
  488. DBG_RETURN(FAIL);
  489. }
  490. /* }}} */
  491. /* {{{ php_mysqlnd_ok_free_mem */
  492. static void
  493. php_mysqlnd_ok_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  494. {
  495. MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet;
  496. if (p->message) {
  497. mnd_efree(p->message);
  498. p->message = NULL;
  499. }
  500. if (!stack_allocation) {
  501. mnd_pefree(p, p->header.persistent);
  502. }
  503. }
  504. /* }}} */
  505. /* {{{ php_mysqlnd_eof_read */
  506. static enum_func_status
  507. php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
  508. {
  509. /*
  510. EOF packet is since 4.1 five bytes long,
  511. but we can get also an error, make it bigger.
  512. Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
  513. */
  514. MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
  515. size_t buf_len = conn->net->cmd_buffer.length;
  516. zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
  517. zend_uchar *p = buf;
  518. zend_uchar *begin = buf;
  519. DBG_ENTER("php_mysqlnd_eof_read");
  520. PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
  521. BAIL_IF_NO_MORE_DATA;
  522. /* Should be always EODATA_MARKER */
  523. packet->field_count = uint1korr(p);
  524. p++;
  525. BAIL_IF_NO_MORE_DATA;
  526. if (ERROR_MARKER == packet->field_count) {
  527. php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
  528. packet->error, sizeof(packet->error),
  529. &packet->error_no, packet->sqlstate
  530. TSRMLS_CC);
  531. DBG_RETURN(PASS);
  532. }
  533. /*
  534. 4.1 sends 1 byte EOF packet after metadata of
  535. PREPARE/EXECUTE but 5 bytes after the result. This is not
  536. according to the Docs@Forge!!!
  537. */
  538. if (packet->header.size > 1) {
  539. packet->warning_count = uint2korr(p);
  540. p+= 2;
  541. BAIL_IF_NO_MORE_DATA;
  542. packet->server_status = uint2korr(p);
  543. p+= 2;
  544. BAIL_IF_NO_MORE_DATA;
  545. } else {
  546. packet->warning_count = 0;
  547. packet->server_status = 0;
  548. }
  549. BAIL_IF_NO_MORE_DATA;
  550. DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u",
  551. packet->field_count, packet->server_status, packet->warning_count);
  552. DBG_RETURN(PASS);
  553. premature_end:
  554. DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
  555. php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
  556. p - begin - packet->header.size);
  557. DBG_RETURN(FAIL);
  558. }
  559. /* }}} */
  560. /* {{{ php_mysqlnd_eof_free_mem */
  561. static
  562. void php_mysqlnd_eof_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  563. {
  564. if (!stack_allocation) {
  565. mnd_pefree(_packet, ((MYSQLND_PACKET_EOF *)_packet)->header.persistent);
  566. }
  567. }
  568. /* }}} */
  569. /* {{{ php_mysqlnd_cmd_write */
  570. size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
  571. {
  572. /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
  573. MYSQLND_PACKET_COMMAND *packet= (MYSQLND_PACKET_COMMAND *) _packet;
  574. MYSQLND_NET *net = conn->net;
  575. unsigned int error_reporting = EG(error_reporting);
  576. size_t written = 0;
  577. DBG_ENTER("php_mysqlnd_cmd_write");
  578. /*
  579. Reset packet_no, or we will get bad handshake!
  580. Every command starts a new TX and packet numbers are reset to 0.
  581. */
  582. net->packet_no = 0;
  583. net->compressed_envelope_packet_no = 0; /* this is for the response */
  584. if (error_reporting) {
  585. EG(error_reporting) = 0;
  586. }
  587. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
  588. #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
  589. net->m.consume_uneaten_data(net, packet->command TSRMLS_CC);
  590. #endif
  591. if (!packet->argument || !packet->arg_len) {
  592. char buffer[MYSQLND_HEADER_SIZE + 1];
  593. int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
  594. written = conn->net->m.send(conn, buffer, 1 TSRMLS_CC);
  595. } else {
  596. size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
  597. zend_uchar *tmp, *p;
  598. tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
  599. if (!tmp) {
  600. goto end;
  601. }
  602. p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
  603. int1store(p, packet->command);
  604. p++;
  605. memcpy(p, packet->argument, packet->arg_len);
  606. ret = conn->net->m.send(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
  607. if (tmp != net->cmd_buffer.buffer) {
  608. MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
  609. mnd_efree(tmp);
  610. }
  611. written = ret;
  612. }
  613. end:
  614. if (error_reporting) {
  615. /* restore error reporting */
  616. EG(error_reporting) = error_reporting;
  617. }
  618. DBG_RETURN(written);
  619. }
  620. /* }}} */
  621. /* {{{ php_mysqlnd_cmd_free_mem */
  622. static
  623. void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  624. {
  625. if (!stack_allocation) {
  626. MYSQLND_PACKET_COMMAND * p = (MYSQLND_PACKET_COMMAND *) _packet;
  627. mnd_pefree(p, p->header.persistent);
  628. }
  629. }
  630. /* }}} */
  631. /* {{{ php_mysqlnd_rset_header_read */
  632. static enum_func_status
  633. php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
  634. {
  635. enum_func_status ret = PASS;
  636. size_t buf_len = conn->net->cmd_buffer.length;
  637. zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
  638. zend_uchar *p = buf;
  639. zend_uchar *begin = buf;
  640. size_t len;
  641. MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
  642. DBG_ENTER("php_mysqlnd_rset_header_read");
  643. PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
  644. BAIL_IF_NO_MORE_DATA;
  645. /*
  646. Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
  647. of encoded sequence for length.
  648. */
  649. if (ERROR_MARKER == *p) {
  650. /* Error */
  651. p++;
  652. BAIL_IF_NO_MORE_DATA;
  653. php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
  654. packet->error_info.error, sizeof(packet->error_info.error),
  655. &packet->error_info.error_no, packet->error_info.sqlstate
  656. TSRMLS_CC);
  657. DBG_RETURN(PASS);
  658. }
  659. packet->field_count = php_mysqlnd_net_field_length(&p);
  660. BAIL_IF_NO_MORE_DATA;
  661. switch (packet->field_count) {
  662. case MYSQLND_NULL_LENGTH:
  663. DBG_INF("LOAD LOCAL");
  664. /*
  665. First byte in the packet is the field count.
  666. Thus, the name is size - 1. And we add 1 for a trailing \0.
  667. Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed
  668. that packet->header.size is > 0. Which means that len can't underflow, that
  669. would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
  670. */
  671. len = packet->header.size - 1;
  672. packet->info_or_local_file = mnd_emalloc(len + 1);
  673. if (packet->info_or_local_file) {
  674. memcpy(packet->info_or_local_file, p, len);
  675. packet->info_or_local_file[len] = '\0';
  676. packet->info_or_local_file_len = len;
  677. } else {
  678. SET_OOM_ERROR(conn->error_info);
  679. ret = FAIL;
  680. }
  681. break;
  682. case 0x00:
  683. DBG_INF("UPSERT");
  684. packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
  685. BAIL_IF_NO_MORE_DATA;
  686. packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
  687. BAIL_IF_NO_MORE_DATA;
  688. packet->server_status = uint2korr(p);
  689. p+=2;
  690. BAIL_IF_NO_MORE_DATA;
  691. packet->warning_count = uint2korr(p);
  692. p+=2;
  693. BAIL_IF_NO_MORE_DATA;
  694. /* Check for additional textual data */
  695. if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
  696. packet->info_or_local_file = mnd_emalloc(len + 1);
  697. if (packet->info_or_local_file) {
  698. memcpy(packet->info_or_local_file, p, len);
  699. packet->info_or_local_file[len] = '\0';
  700. packet->info_or_local_file_len = len;
  701. } else {
  702. SET_OOM_ERROR(conn->error_info);
  703. ret = FAIL;
  704. }
  705. }
  706. DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%u warning_count=%u",
  707. packet->affected_rows, packet->last_insert_id,
  708. packet->server_status, packet->warning_count);
  709. break;
  710. default:
  711. DBG_INF("SELECT");
  712. /* Result set */
  713. break;
  714. }
  715. BAIL_IF_NO_MORE_DATA;
  716. DBG_RETURN(ret);
  717. premature_end:
  718. DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
  719. php_error_docref(NULL TSRMLS_CC, E_WARNING, "RSET_HEADER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
  720. p - begin - packet->header.size);
  721. DBG_RETURN(FAIL);
  722. }
  723. /* }}} */
  724. /* {{{ php_mysqlnd_rset_header_free_mem */
  725. static
  726. void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  727. {
  728. MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
  729. DBG_ENTER("php_mysqlnd_rset_header_free_mem");
  730. if (p->info_or_local_file) {
  731. mnd_efree(p->info_or_local_file);
  732. p->info_or_local_file = NULL;
  733. }
  734. if (!stack_allocation) {
  735. mnd_pefree(p, p->header.persistent);
  736. }
  737. DBG_VOID_RETURN;
  738. }
  739. /* }}} */
  740. static size_t rset_field_offsets[] =
  741. {
  742. STRUCT_OFFSET(MYSQLND_FIELD, catalog),
  743. STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
  744. STRUCT_OFFSET(MYSQLND_FIELD, db),
  745. STRUCT_OFFSET(MYSQLND_FIELD, db_length),
  746. STRUCT_OFFSET(MYSQLND_FIELD, table),
  747. STRUCT_OFFSET(MYSQLND_FIELD, table_length),
  748. STRUCT_OFFSET(MYSQLND_FIELD, org_table),
  749. STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
  750. STRUCT_OFFSET(MYSQLND_FIELD, name),
  751. STRUCT_OFFSET(MYSQLND_FIELD, name_length),
  752. STRUCT_OFFSET(MYSQLND_FIELD, org_name),
  753. STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
  754. };
  755. /* {{{ php_mysqlnd_rset_field_read */
  756. static enum_func_status
  757. php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
  758. {
  759. /* Should be enough for the metadata of a single row */
  760. MYSQLND_PACKET_RES_FIELD *packet= (MYSQLND_PACKET_RES_FIELD *) _packet;
  761. size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
  762. zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
  763. zend_uchar *p = buf;
  764. zend_uchar *begin = buf;
  765. char *root_ptr;
  766. unsigned long len;
  767. MYSQLND_FIELD *meta;
  768. unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
  769. DBG_ENTER("php_mysqlnd_rset_field_read");
  770. PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
  771. if (packet->skip_parsing) {
  772. DBG_RETURN(PASS);
  773. }
  774. BAIL_IF_NO_MORE_DATA;
  775. if (ERROR_MARKER == *p) {
  776. /* Error */
  777. p++;
  778. BAIL_IF_NO_MORE_DATA;
  779. php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
  780. packet->error_info.error, sizeof(packet->error_info.error),
  781. &packet->error_info.error_no, packet->error_info.sqlstate
  782. TSRMLS_CC);
  783. DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
  784. DBG_RETURN(PASS);
  785. } else if (EODATA_MARKER == *p && packet->header.size < 8) {
  786. /* Premature EOF. That should be COM_FIELD_LIST */
  787. DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
  788. packet->stupid_list_fields_eof = TRUE;
  789. DBG_RETURN(PASS);
  790. }
  791. meta = packet->metadata;
  792. for (i = 0; i < field_count; i += 2) {
  793. len = php_mysqlnd_net_field_length(&p);
  794. BAIL_IF_NO_MORE_DATA;
  795. switch ((len)) {
  796. case 0:
  797. *(const char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
  798. *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
  799. break;
  800. case MYSQLND_NULL_LENGTH:
  801. goto faulty_or_fake;
  802. default:
  803. *(const char **)(((char *)meta) + rset_field_offsets[i]) = (const char *)p;
  804. *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
  805. p += len;
  806. total_len += len + 1;
  807. break;
  808. }
  809. BAIL_IF_NO_MORE_DATA;
  810. }
  811. /* 1 byte filler */
  812. p++;
  813. BAIL_IF_NO_MORE_DATA;
  814. meta->charsetnr = uint2korr(p);
  815. p += 2;
  816. BAIL_IF_NO_MORE_DATA;
  817. meta->length = uint4korr(p);
  818. p += 4;
  819. BAIL_IF_NO_MORE_DATA;
  820. meta->type = uint1korr(p);
  821. p += 1;
  822. BAIL_IF_NO_MORE_DATA;
  823. meta->flags = uint2korr(p);
  824. p += 2;
  825. BAIL_IF_NO_MORE_DATA;
  826. meta->decimals = uint2korr(p);
  827. p += 1;
  828. BAIL_IF_NO_MORE_DATA;
  829. /* 2 byte filler */
  830. p +=2;
  831. BAIL_IF_NO_MORE_DATA;
  832. /* Should we set NUM_FLAG (libmysql does it) ? */
  833. if (
  834. (meta->type <= MYSQL_TYPE_INT24 &&
  835. (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
  836. ) || meta->type == MYSQL_TYPE_YEAR)
  837. {
  838. meta->flags |= NUM_FLAG;
  839. }
  840. /*
  841. def could be empty, thus don't allocate on the root.
  842. NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
  843. Otherwise the string is length encoded.
  844. */
  845. if (packet->header.size > (size_t) (p - buf) &&
  846. (len = php_mysqlnd_net_field_length(&p)) &&
  847. len != MYSQLND_NULL_LENGTH)
  848. {
  849. BAIL_IF_NO_MORE_DATA;
  850. DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
  851. meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
  852. if (!meta->def) {
  853. SET_OOM_ERROR(conn->error_info);
  854. DBG_RETURN(FAIL);
  855. }
  856. memcpy(meta->def, p, len);
  857. meta->def[len] = '\0';
  858. meta->def_length = len;
  859. p += len;
  860. }
  861. DBG_INF_FMT("allocing root. persistent=%u", packet->persistent_alloc);
  862. root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
  863. if (!root_ptr) {
  864. SET_OOM_ERROR(conn->error_info);
  865. DBG_RETURN(FAIL);
  866. }
  867. meta->root_len = total_len;
  868. /* Now do allocs */
  869. if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
  870. len = meta->catalog_length;
  871. meta->catalog = memcpy(root_ptr, meta->catalog, len);
  872. *(root_ptr +=len) = '\0';
  873. root_ptr++;
  874. }
  875. if (meta->db && meta->db != mysqlnd_empty_string) {
  876. len = meta->db_length;
  877. meta->db = memcpy(root_ptr, meta->db, len);
  878. *(root_ptr +=len) = '\0';
  879. root_ptr++;
  880. }
  881. if (meta->table && meta->table != mysqlnd_empty_string) {
  882. len = meta->table_length;
  883. meta->table = memcpy(root_ptr, meta->table, len);
  884. *(root_ptr +=len) = '\0';
  885. root_ptr++;
  886. }
  887. if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
  888. len = meta->org_table_length;
  889. meta->org_table = memcpy(root_ptr, meta->org_table, len);
  890. *(root_ptr +=len) = '\0';
  891. root_ptr++;
  892. }
  893. if (meta->name && meta->name != mysqlnd_empty_string) {
  894. len = meta->name_length;
  895. meta->name = memcpy(root_ptr, meta->name, len);
  896. *(root_ptr +=len) = '\0';
  897. root_ptr++;
  898. }
  899. if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
  900. len = meta->org_name_length;
  901. meta->org_name = memcpy(root_ptr, meta->org_name, len);
  902. *(root_ptr +=len) = '\0';
  903. root_ptr++;
  904. }
  905. DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
  906. meta->name? meta->name:"*NA*");
  907. DBG_RETURN(PASS);
  908. faulty_or_fake:
  909. DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
  910. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
  911. " The server is faulty");
  912. DBG_RETURN(FAIL);
  913. premature_end:
  914. DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
  915. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
  916. "shorter than expected", p - begin - packet->header.size);
  917. DBG_RETURN(FAIL);
  918. }
  919. /* }}} */
  920. /* {{{ php_mysqlnd_rset_field_free_mem */
  921. static
  922. void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
  923. {
  924. MYSQLND_PACKET_RES_FIELD *p= (MYSQLND_PACKET_RES_FIELD *) _packet;
  925. /* p->metadata was passed to us as temporal buffer */
  926. if (!stack_allocation) {
  927. mnd_pefree(p, p->header.persistent);
  928. }
  929. }
  930. /* }}} */
  931. /* {{{ php_mysqlnd_read_row_ex */
  932. static enum_func_status
  933. php_mysqlnd_read_row_ex(MYSQLND * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
  934. MYSQLND_MEMORY_POOL_CHUNK **buffer,
  935. size_t *data_size, zend_bool persistent_alloc,
  936. unsigned int prealloc_more_bytes TSRMLS_DC)
  937. {
  938. enum_func_status ret = PASS;
  939. MYSQLND_PACKET_HEADER header;
  940. zend_uchar *p = NULL;
  941. zend_bool first_iteration = TRUE;
  942. DBG_ENTER("php_mysqlnd_read_row_ex");
  943. /*
  944. To ease the process the server splits everything in packets up to 2^24 - 1.
  945. Even in the case the payload is evenly divisible by this value, the last
  946. packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
  947. for next one if they have 2^24 - 1 sizes. But just read the header of a
  948. zero-length byte, don't read the body, there is no such.
  949. */
  950. *data_size = prealloc_more_bytes;
  951. while (1) {
  952. if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) {
  953. ret = FAIL;
  954. break;
  955. }
  956. *data_size += header.size;
  957. if (first_iteration) {
  958. first_iteration = FALSE;
  959. /*
  960. We need a trailing \0 for the last string, in case of text-mode,
  961. to be able to implement read-only variables. Thus, we add + 1.
  962. */
  963. *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
  964. if (!*buffer) {
  965. ret = FAIL;
  966. break;
  967. }
  968. p = (*buffer)->ptr;
  969. } else if (!first_iteration) {
  970. /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
  971. if (!header.size) {
  972. break;
  973. }
  974. /*
  975. We have to realloc the buffer.
  976. We need a trailing \0 for the last string, in case of text-mode,
  977. to be able to implement read-only variables.
  978. */
  979. if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC)) {
  980. SET_OOM_ERROR(conn->error_info);
  981. ret = FAIL;
  982. break;
  983. }
  984. /* The position could have changed, recalculate */
  985. p = (*buffer)->ptr + (*data_size - header.size);
  986. }
  987. if (PASS != (ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) {
  988. DBG_ERR("Empty row packet body");
  989. php_error(E_WARNING, "Empty row packet body");
  990. break;
  991. }
  992. if (header.size < MYSQLND_MAX_PACKET_SIZE) {
  993. break;
  994. }
  995. }
  996. if (ret == FAIL && *buffer) {
  997. (*buffer)->free_chunk((*buffer) TSRMLS_CC);
  998. *buffer = NULL;
  999. }
  1000. *data_size -= prealloc_more_bytes;
  1001. DBG_RETURN(ret);
  1002. }
  1003. /* }}} */
  1004. /* {{{ php_mysqlnd_rowp_read_binary_protocol */
  1005. enum_func_status
  1006. php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
  1007. unsigned int field_count, MYSQLND_FIELD *fields_metadata,
  1008. zend_bool persistent,
  1009. zend_bool as_unicode, zend_bool as_int_or_float,
  1010. MYSQLND_STATS * stats TSRMLS_DC)
  1011. {
  1012. unsigned int i;
  1013. zend_uchar *p = row_buffer->ptr;
  1014. zend_uchar *null_ptr, bit;
  1015. zval **current_field, **end_field, **start_field;
  1016. DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
  1017. if (!fields) {
  1018. DBG_RETURN(FAIL);
  1019. }
  1020. end_field = (start_field = fields) + field_count;
  1021. /* skip the first byte, not EODATA_MARKER -> 0x0, status */
  1022. p++;
  1023. null_ptr= p;
  1024. p += (field_count + 9)/8; /* skip null bits */
  1025. bit = 4; /* first 2 bits are reserved */
  1026. for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
  1027. DBG_INF("Directly creating zval");
  1028. MAKE_STD_ZVAL(*current_field);
  1029. if (!*current_field) {
  1030. DBG_RETURN(FAIL);
  1031. }
  1032. }
  1033. for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
  1034. enum_mysqlnd_collected_stats statistic;
  1035. zend_uchar * orig_p = p;
  1036. DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u as_unicode=%u",
  1037. *current_field, i,
  1038. fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
  1039. fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
  1040. if (*null_ptr & bit) {
  1041. DBG_INF("It's null");
  1042. ZVAL_NULL(*current_field);
  1043. statistic = STAT_BINARY_TYPE_FETCHED_NULL;
  1044. } else {
  1045. enum_mysqlnd_field_types type = fields_metadata[i].type;
  1046. mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], 0, &p, as_unicode TSRMLS_CC);
  1047. if (MYSQLND_G(collect_statistics)) {
  1048. switch (fields_metadata[i].type) {
  1049. case MYSQL_TYPE_DECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
  1050. case MYSQL_TYPE_TINY: statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
  1051. case MYSQL_TYPE_SHORT: statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
  1052. case MYSQL_TYPE_LONG: statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
  1053. case MYSQL_TYPE_FLOAT: statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
  1054. case MYSQL_TYPE_DOUBLE: statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
  1055. case MYSQL_TYPE_NULL: statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
  1056. case MYSQL_TYPE_TIMESTAMP: statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
  1057. case MYSQL_TYPE_LONGLONG: statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
  1058. case MYSQL_TYPE_INT24: statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
  1059. case MYSQL_TYPE_DATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
  1060. case MYSQL_TYPE_TIME: statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
  1061. case MYSQL_TYPE_DATETIME: statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
  1062. case MYSQL_TYPE_YEAR: statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
  1063. case MYSQL_TYPE_NEWDATE: statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
  1064. case MYSQL_TYPE_VARCHAR: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
  1065. case MYSQL_TYPE_BIT: statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
  1066. case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
  1067. case MYSQL_TYPE_ENUM: statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
  1068. case MYSQL_TYPE_SET: statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
  1069. case MYSQL_TYPE_TINY_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
  1070. case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
  1071. case MYSQL_TYPE_LONG_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
  1072. case MYSQL_TYPE_BLOB: statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
  1073. case MYSQL_TYPE_VAR_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
  1074. case MYSQL_TYPE_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
  1075. case MYSQL_TYPE_GEOMETRY: statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
  1076. default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
  1077. }
  1078. }
  1079. }
  1080. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
  1081. STAT_BYTES_RECEIVED_PURE_DATA_PS,
  1082. (Z_TYPE_PP(current_field) == IS_STRING)?
  1083. Z_STRLEN_PP(current_field) : (p - orig_p));
  1084. if (!((bit<<=1) & 255)) {
  1085. bit = 1; /* to the following byte */
  1086. null_ptr++;
  1087. }
  1088. }
  1089. DBG_RETURN(PASS);
  1090. }
  1091. /* }}} */
  1092. /* {{{ php_mysqlnd_rowp_read_text_protocol */
  1093. enum_func_status
  1094. php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
  1095. unsigned int field_count, MYSQLND_FIELD *fields_metadata,
  1096. zend_bool persistent,
  1097. zend_bool as_unicode, zend_bool as_int_or_float,
  1098. MYSQLND_STATS * stats TSRMLS_DC)
  1099. {
  1100. unsigned int i;
  1101. zend_bool last_field_was_string = FALSE;
  1102. zval **current_field, **end_field, **start_field;
  1103. zend_uchar *p = row_buffer->ptr;
  1104. size_t data_size = row_buffer->app;
  1105. zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
  1106. DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
  1107. if (!fields) {
  1108. DBG_RETURN(FAIL);
  1109. }
  1110. end_field = (start_field = fields) + field_count;
  1111. for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
  1112. DBG_INF("Directly creating zval");
  1113. MAKE_STD_ZVAL(*current_field);
  1114. if (!*current_field) {
  1115. DBG_RETURN(FAIL);
  1116. }
  1117. }
  1118. for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
  1119. /* Don't reverse the order. It is significant!*/
  1120. zend_uchar *this_field_len_pos = p;
  1121. /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
  1122. unsigned long len = php_mysqlnd_net_field_length(&p);
  1123. if (current_field > start_field && last_field_was_string) {
  1124. /*
  1125. Normal queries:
  1126. We have to put \0 now to the end of the previous field, if it was
  1127. a string. IS_NULL doesn't matter. Because we have already read our
  1128. length, then we can overwrite it in the row buffer.
  1129. This statement terminates the previous field, not the current one.
  1130. NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
  1131. Any string's length is encoded in at least one byte, so we can stick
  1132. a \0 there.
  1133. */
  1134. *this_field_len_pos = '\0';
  1135. }
  1136. /* NULL or NOT NULL, this is the question! */
  1137. if (len == MYSQLND_NULL_LENGTH) {
  1138. ZVAL_NULL(*current_field);
  1139. last_field_was_string = FALSE;
  1140. } else {
  1141. #if MYSQLND_UNICODE || defined(MYSQLND_STRING_TO_INT_CONVERSION)
  1142. struct st_mysqlnd_perm_bind perm_bind =
  1143. mysqlnd_ps_fetch_functions[fields_metadata[i].type];
  1144. #endif
  1145. if (MYSQLND_G(collect_statistics)) {
  1146. enum_mysqlnd_collected_stats statistic;
  1147. switch (fields_metadata[i].type) {
  1148. case MYSQL_TYPE_DECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
  1149. case MYSQL_TYPE_TINY: statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
  1150. case MYSQL_TYPE_SHORT: statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
  1151. case MYSQL_TYPE_LONG: statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
  1152. case MYSQL_TYPE_FLOAT: statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
  1153. case MYSQL_TYPE_DOUBLE: statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
  1154. case MYSQL_TYPE_NULL: statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
  1155. case MYSQL_TYPE_TIMESTAMP: statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
  1156. case MYSQL_TYPE_LONGLONG: statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
  1157. case MYSQL_TYPE_INT24: statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
  1158. case MYSQL_TYPE_DATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
  1159. case MYSQL_TYPE_TIME: statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
  1160. case MYSQL_TYPE_DATETIME: statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
  1161. case MYSQL_TYPE_YEAR: statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
  1162. case MYSQL_TYPE_NEWDATE: statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
  1163. case MYSQL_TYPE_VARCHAR: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
  1164. case MYSQL_TYPE_BIT: statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
  1165. case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
  1166. case MYSQL_TYPE_ENUM: statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
  1167. case MYSQL_TYPE_SET: statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
  1168. case MYSQL_TYPE_TINY_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
  1169. case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
  1170. case MYSQL_TYPE_LONG_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
  1171. case MYSQL_TYPE_BLOB: statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
  1172. case MYSQL_TYPE_VAR_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
  1173. case MYSQL_TYPE_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
  1174. case MYSQL_TYPE_GEOMETRY: statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
  1175. default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
  1176. }
  1177. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
  1178. }
  1179. #ifdef MYSQLND_STRING_TO_INT_CONVERSION
  1180. if (as_int_or_float && perm_bind.php_type == IS_LONG) {
  1181. zend_uchar save = *(p + len);
  1182. /* We have to make it ASCIIZ temporarily */
  1183. *(p + len) = '\0';
  1184. if (perm_bind.pack_len < SIZEOF_LONG) {
  1185. /* direct conversion */
  1186. int64_t v =
  1187. #ifndef PHP_WIN32
  1188. atoll((char *) p);
  1189. #else
  1190. _atoi64((char *) p);
  1191. #endif
  1192. ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
  1193. } else {
  1194. uint64_t v =
  1195. #ifndef PHP_WIN32
  1196. (uint64_t) atoll((char *) p);
  1197. #else
  1198. (uint64_t) _atoi64((char *) p);
  1199. #endif
  1200. zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
  1201. /* We have to make it ASCIIZ temporarily */
  1202. #if SIZEOF_LONG==8
  1203. if (uns == TRUE && v > 9223372036854775807L)
  1204. #elif SIZEOF_LONG==4
  1205. if ((uns == TRUE && v > L64(2147483647)) ||
  1206. (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
  1207. (L64(-2147483648) > (int64_t) v))))
  1208. #else
  1209. #error Need fix for this architecture
  1210. #endif /* SIZEOF */
  1211. {
  1212. ZVAL_STRINGL(*current_field, (char *)p, len, 0);
  1213. } else {
  1214. ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
  1215. }
  1216. }
  1217. *(p + len) = save;
  1218. } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
  1219. zend_uchar save = *(p + len);
  1220. /* We have to make it ASCIIZ temporarily */
  1221. *(p + len) = '\0';
  1222. ZVAL_DOUBLE(*current_field, atof((char *) p));
  1223. *(p + len) = save;
  1224. } else
  1225. #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
  1226. if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
  1227. /*
  1228. BIT fields are specially handled. As they come as bit mask, we have
  1229. to convert it to human-readable representation. As the bits take
  1230. less space in the protocol than the numbers they represent, we don't
  1231. have enough space in the packet buffer to overwrite inside.
  1232. Thus, a bit more space is pre-allocated at the end of the buffer,
  1233. see php_mysqlnd_rowp_read(). And we add the strings at the end.
  1234. Definitely not nice, _hackish_ :(, but works.
  1235. */
  1236. zend_uchar *start = bit_area;
  1237. ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, as_unicode, len TSRMLS_CC);
  1238. /*
  1239. We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
  1240. later in this function there will be an advancement.
  1241. */
  1242. p -= len;
  1243. if (Z_TYPE_PP(current_field) == IS_LONG) {
  1244. bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
  1245. #if MYSQLND_UNICODE
  1246. if (as_unicode) {
  1247. ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
  1248. } else
  1249. #endif
  1250. {
  1251. ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
  1252. }
  1253. } else if (Z_TYPE_PP(current_field) == IS_STRING){
  1254. memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
  1255. bit_area += Z_STRLEN_PP(current_field);
  1256. *bit_area++ = '\0';
  1257. zval_dtor(*current_field);
  1258. #if MYSQLND_UNICODE
  1259. if (as_unicode) {
  1260. ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
  1261. } else
  1262. #endif
  1263. {
  1264. ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
  1265. }
  1266. }
  1267. /*
  1268. IS_UNICODE should not be specially handled. In unicode mode
  1269. the buffers are not referenced - everything is copied.
  1270. */
  1271. } else
  1272. #if MYSQLND_UNICODE == 0
  1273. {
  1274. ZVAL_STRINGL(*current_field, (char *)p, len, 0);
  1275. }
  1276. #else
  1277. /*
  1278. Here we have to convert to UTF16, which means not reusing the buffer.
  1279. Which in turn means that we can free the buffers once we have
  1280. stored the result set, if we use store_result().
  1281. Also the destruction of the zvals should not call zval_copy_ctor()
  1282. because then we will leak.
  1283. XXX: Keep in mind that up there there is an open `else` in
  1284. #ifdef MYSQLND_STRING_TO_INT_CONVERSION
  1285. which will make with this `if` an `else if`.
  1286. */
  1287. if ((perm_bind.is_possibly_blob == TRUE &&
  1288. fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
  1289. (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
  1290. {
  1291. /* BLOB - no conversion please */
  1292. ZVAL_STRINGL(*current_field, (char *)p, len, 0);
  1293. } else {
  1294. ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
  1295. }
  1296. #endif
  1297. p += len;
  1298. last_field_was_string = TRUE;
  1299. }
  1300. }
  1301. if (last_field_was_string) {
  1302. /* Normal queries: The buffer has one more byte at the end, because we need it */
  1303. row_buffer->ptr[data_size] = '\0';
  1304. }
  1305. DBG_RETURN(PASS);
  1306. }
  1307. /* }}} */
  1308. /* {{{ php_mysqlnd_rowp_read */
  1309. /*
  1310. if normal statements => packet->fields is created by this function,
  1311. if PS => packet->fields is passed from outside
  1312. */
  1313. static enum_func_status
  1314. php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
  1315. {
  1316. MYSQLND_NET *net = conn->net;
  1317. zend_uchar *p;
  1318. enum_func_status ret = PASS;
  1319. size_t old_chunk_size = net->stream->chunk_size;
  1320. MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
  1321. size_t post_alloc_for_bit_fields = 0;
  1322. size_t data_size = 0;
  1323. DBG_ENTER("php_mysqlnd_rowp_read");
  1324. if (!packet->binary_protocol && packet->bit_fields_count) {
  1325. /* For every field we need terminating \0 */
  1326. post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
  1327. }
  1328. ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
  1329. packet->persistent_alloc, post_alloc_for_bit_fields
  1330. TSRMLS_CC);
  1331. if (FAIL == ret) {
  1332. goto end;
  1333. }
  1334. MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
  1335. MYSQLND_HEADER_SIZE + packet->header.size,
  1336. packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
  1337. 1);
  1338. /* packet->row_buffer->ptr is of size 'data_size + 1' */
  1339. packet->header.size = data_size;
  1340. packet->row_buffer->app = data_size;
  1341. if (ERROR_MARKER == (*(p = packet->row_buffer->ptr))) {
  1342. /*
  1343. Error message as part of the result set,
  1344. not good but we should not hang. See:
  1345. Bug #27876 : SF with cyrillic variable name fails during execution
  1346. */
  1347. ret = FAIL;
  1348. php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
  1349. packet->error_info.error,
  1350. sizeof(packet->error_info.error),
  1351. &packet->error_info.error_no,
  1352. packet->error_info.sqlstate
  1353. TSRMLS_CC);
  1354. } else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
  1355. packet->eof = TRUE;
  1356. p++;
  1357. if (data_size > 1) {
  1358. packet->warning_count = uint2korr(p);
  1359. p += 2;
  1360. packet->server_status = uint2korr(p);
  1361. /* Seems we have 3 bytes reserved for future use */
  1362. DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
  1363. }
  1364. } else {
  1365. MYSQLND_INC_CONN_STATISTIC(conn->stats,
  1366. packet->binary_protocol

Large files files are truncated, but you can click here to view the full file