deserialization.hpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. //
  2. // Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZATION_HPP
  8. #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZATION_HPP
  9. #include <boost/mysql/client_errc.hpp>
  10. #include <boost/mysql/column_type.hpp>
  11. #include <boost/mysql/common_server_errc.hpp>
  12. #include <boost/mysql/diagnostics.hpp>
  13. #include <boost/mysql/error_categories.hpp>
  14. #include <boost/mysql/error_code.hpp>
  15. #include <boost/mysql/field_kind.hpp>
  16. #include <boost/mysql/field_view.hpp>
  17. #include <boost/mysql/metadata_collection_view.hpp>
  18. #include <boost/mysql/string_view.hpp>
  19. #include <boost/mysql/detail/coldef_view.hpp>
  20. #include <boost/mysql/detail/config.hpp>
  21. #include <boost/mysql/detail/ok_view.hpp>
  22. #include <boost/mysql/detail/resultset_encoding.hpp>
  23. #include <boost/mysql/impl/internal/error/server_error_to_string.hpp>
  24. #include <boost/mysql/impl/internal/protocol/capabilities.hpp>
  25. #include <boost/mysql/impl/internal/protocol/db_flavor.hpp>
  26. #include <boost/mysql/impl/internal/protocol/impl/binary_protocol.hpp>
  27. #include <boost/mysql/impl/internal/protocol/impl/deserialization_context.hpp>
  28. #include <boost/mysql/impl/internal/protocol/impl/null_bitmap.hpp>
  29. #include <boost/mysql/impl/internal/protocol/impl/protocol_field_type.hpp>
  30. #include <boost/mysql/impl/internal/protocol/impl/text_protocol.hpp>
  31. #include <boost/mysql/impl/internal/protocol/static_buffer.hpp>
  32. #include <boost/config.hpp>
  33. #include <boost/core/ignore_unused.hpp>
  34. #include <boost/core/span.hpp>
  35. #include <cstddef>
  36. #include <cstdint>
  37. namespace boost {
  38. namespace mysql {
  39. namespace detail {
  40. // OK packets (views because strings are non-owning)
  41. inline error_code deserialize_ok_packet(span<const std::uint8_t> msg, ok_view& output); // for testing
  42. // Error packets (exposed for testing)
  43. struct err_view
  44. {
  45. std::uint16_t error_code;
  46. string_view error_message;
  47. };
  48. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_error_packet(
  49. span<const std::uint8_t> message,
  50. err_view& pack,
  51. bool has_sql_state = true
  52. );
  53. BOOST_ATTRIBUTE_NODISCARD inline error_code process_error_packet(
  54. span<const std::uint8_t> message,
  55. db_flavor flavor,
  56. diagnostics& diag,
  57. bool has_sql_state = true
  58. );
  59. // Deserializes a response that may be an OK or an error packet.
  60. // Applicable for commands like ping and reset connection.
  61. // If the response is an OK packet, sets backslash_escapes according to the
  62. // OK packet's server status flags
  63. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_ok_response(
  64. span<const std::uint8_t> message,
  65. db_flavor flavor,
  66. diagnostics& diag,
  67. bool& backslash_escapes
  68. );
  69. // Column definition
  70. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_column_definition(
  71. span<const std::uint8_t> input,
  72. coldef_view& output
  73. );
  74. // Prepare statement response
  75. struct prepare_stmt_response
  76. {
  77. std::uint32_t id;
  78. std::uint16_t num_columns;
  79. std::uint16_t num_params;
  80. };
  81. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_prepare_stmt_response_impl(
  82. span<const std::uint8_t> message,
  83. prepare_stmt_response& output
  84. ); // exposed for testing, doesn't take header into account
  85. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_prepare_stmt_response(
  86. span<const std::uint8_t> message,
  87. db_flavor flavor,
  88. prepare_stmt_response& output,
  89. diagnostics& diag
  90. );
  91. // Execution messages
  92. struct execute_response
  93. {
  94. enum class type_t
  95. {
  96. num_fields,
  97. ok_packet,
  98. error
  99. } type;
  100. union data_t
  101. {
  102. std::size_t num_fields;
  103. ok_view ok_pack;
  104. error_code err;
  105. data_t(size_t v) noexcept : num_fields(v) {}
  106. data_t(const ok_view& v) noexcept : ok_pack(v) {}
  107. data_t(error_code v) noexcept : err(v) {}
  108. } data;
  109. execute_response(std::size_t v) noexcept : type(type_t::num_fields), data(v) {}
  110. execute_response(const ok_view& v) noexcept : type(type_t::ok_packet), data(v) {}
  111. execute_response(error_code v) noexcept : type(type_t::error), data(v) {}
  112. };
  113. inline execute_response deserialize_execute_response(
  114. span<const std::uint8_t> msg,
  115. db_flavor flavor,
  116. diagnostics& diag
  117. );
  118. struct row_message
  119. {
  120. enum class type_t
  121. {
  122. row,
  123. ok_packet,
  124. error
  125. } type;
  126. union data_t
  127. {
  128. span<const std::uint8_t> row;
  129. ok_view ok_pack;
  130. error_code err;
  131. data_t(span<const std::uint8_t> row) noexcept : row(row) {}
  132. data_t(const ok_view& ok_pack) noexcept : ok_pack(ok_pack) {}
  133. data_t(error_code err) noexcept : err(err) {}
  134. } data;
  135. row_message(span<const std::uint8_t> row) noexcept : type(type_t::row), data(row) {}
  136. row_message(const ok_view& ok_pack) noexcept : type(type_t::ok_packet), data(ok_pack) {}
  137. row_message(error_code v) noexcept : type(type_t::error), data(v) {}
  138. };
  139. inline row_message deserialize_row_message(span<const std::uint8_t> msg, db_flavor flavor, diagnostics& diag);
  140. inline error_code deserialize_row(
  141. resultset_encoding encoding,
  142. span<const std::uint8_t> message,
  143. metadata_collection_view meta,
  144. span<field_view> output // Should point to meta.size() field_view objects
  145. );
  146. // Server hello
  147. struct server_hello
  148. {
  149. using auth_buffer_type = static_buffer<8 + 0xff>;
  150. db_flavor server;
  151. std::uint32_t connection_id{};
  152. auth_buffer_type auth_plugin_data;
  153. capabilities server_capabilities{};
  154. string_view auth_plugin_name;
  155. };
  156. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_server_hello_impl(
  157. span<const std::uint8_t> msg,
  158. server_hello& output
  159. ); // exposed for testing, doesn't take message header into account
  160. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_server_hello(
  161. span<const std::uint8_t> msg,
  162. server_hello& output,
  163. diagnostics& diag
  164. );
  165. // Auth switch
  166. struct auth_switch
  167. {
  168. string_view plugin_name;
  169. span<const std::uint8_t> auth_data;
  170. };
  171. BOOST_ATTRIBUTE_NODISCARD inline error_code deserialize_auth_switch(
  172. span<const std::uint8_t> msg,
  173. auth_switch& output
  174. ); // exposed for testing
  175. // Handshake server response
  176. struct handshake_server_response
  177. {
  178. enum class type_t
  179. {
  180. ok,
  181. error,
  182. auth_switch,
  183. auth_more_data
  184. } type;
  185. union data_t
  186. {
  187. ok_view ok;
  188. error_code err;
  189. auth_switch auth_sw;
  190. span<const std::uint8_t> more_data;
  191. data_t(const ok_view& ok) noexcept : ok(ok) {}
  192. data_t(error_code err) noexcept : err(err) {}
  193. data_t(auth_switch msg) noexcept : auth_sw(msg) {}
  194. data_t(span<const std::uint8_t> more_data) noexcept : more_data(more_data) {}
  195. } data;
  196. handshake_server_response(const ok_view& ok) noexcept : type(type_t::ok), data(ok) {}
  197. handshake_server_response(error_code err) noexcept : type(type_t::error), data(err) {}
  198. handshake_server_response(auth_switch auth_switch) noexcept : type(type_t::auth_switch), data(auth_switch)
  199. {
  200. }
  201. handshake_server_response(span<const std::uint8_t> more_data) noexcept
  202. : type(type_t::auth_more_data), data(more_data)
  203. {
  204. }
  205. };
  206. inline handshake_server_response deserialize_handshake_server_response(
  207. span<const std::uint8_t> buff,
  208. db_flavor flavor,
  209. diagnostics& diag
  210. );
  211. } // namespace detail
  212. } // namespace mysql
  213. } // namespace boost
  214. //
  215. // Implementations
  216. //
  217. namespace boost {
  218. namespace mysql {
  219. namespace detail {
  220. // Constants
  221. BOOST_INLINE_CONSTEXPR std::uint8_t error_packet_header = 0xff;
  222. BOOST_INLINE_CONSTEXPR std::uint8_t ok_packet_header = 0x00;
  223. } // namespace detail
  224. } // namespace mysql
  225. } // namespace boost
  226. //
  227. // Deserialization
  228. //
  229. // OK packets
  230. boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet(
  231. span<const std::uint8_t> msg,
  232. ok_view& output
  233. )
  234. {
  235. struct ok_packet
  236. {
  237. // header: int<1> header 0x00 or 0xFE the OK packet header
  238. int_lenenc affected_rows;
  239. int_lenenc last_insert_id;
  240. int2 status_flags; // server_status_flags
  241. int2 warnings;
  242. // CLIENT_SESSION_TRACK: not implemented
  243. string_lenenc info;
  244. } pack{};
  245. deserialization_context ctx(msg);
  246. auto err = ctx.deserialize(pack.affected_rows, pack.last_insert_id, pack.status_flags, pack.warnings);
  247. if (err != deserialize_errc::ok)
  248. return to_error_code(err);
  249. if (ctx.enough_size(1)) // message is optional, may be omitted
  250. {
  251. err = pack.info.deserialize(ctx);
  252. if (err != deserialize_errc::ok)
  253. return to_error_code(err);
  254. }
  255. output = {
  256. pack.affected_rows.value,
  257. pack.last_insert_id.value,
  258. pack.status_flags.value,
  259. pack.warnings.value,
  260. pack.info.value,
  261. };
  262. return ctx.check_extra_bytes();
  263. }
  264. // Error packets
  265. boost::mysql::error_code boost::mysql::detail::deserialize_error_packet(
  266. span<const std::uint8_t> msg,
  267. err_view& output,
  268. bool has_sql_state
  269. )
  270. {
  271. struct err_packet
  272. {
  273. // int1 header 0xFF ERR packet header
  274. int2 error_code;
  275. // if capabilities & CLIENT_PROTOCOL_41 { (modeled here as has_sql_state)
  276. string_fixed<1> sql_state_marker;
  277. string_fixed<5> sql_state;
  278. // }
  279. string_eof error_message;
  280. } pack{};
  281. deserialization_context ctx(msg);
  282. auto err = has_sql_state ? ctx.deserialize(
  283. pack.error_code,
  284. pack.sql_state_marker,
  285. pack.sql_state,
  286. pack.error_message
  287. )
  288. : ctx.deserialize(pack.error_code, pack.error_message);
  289. if (err != deserialize_errc::ok)
  290. return to_error_code(err);
  291. output = err_view{
  292. pack.error_code.value,
  293. pack.error_message.value,
  294. };
  295. return ctx.check_extra_bytes();
  296. }
  297. boost::mysql::error_code boost::mysql::detail::process_error_packet(
  298. span<const std::uint8_t> msg,
  299. db_flavor flavor,
  300. diagnostics& diag,
  301. bool has_sql_state
  302. )
  303. {
  304. err_view error_packet{};
  305. auto err = deserialize_error_packet(msg, error_packet, has_sql_state);
  306. if (err)
  307. return err;
  308. // Error message
  309. access::get_impl(diag).assign_server(error_packet.error_message);
  310. // Error code
  311. if (common_error_to_string(error_packet.error_code))
  312. {
  313. // This is an error shared between MySQL and MariaDB, represented as a common_server_errc.
  314. // get_common_error_message will check that the code has a common_server_errc representation
  315. // (the common error range has "holes" because of removed error codes)
  316. return static_cast<common_server_errc>(error_packet.error_code);
  317. }
  318. else
  319. {
  320. // This is a MySQL or MariaDB specific code. There is no fixed list of error codes,
  321. // as they both keep adding more codes, so no validation happens.
  322. const auto& cat = flavor == db_flavor::mysql ? get_mysql_server_category()
  323. : get_mariadb_server_category();
  324. return error_code(error_packet.error_code, cat);
  325. }
  326. }
  327. // Column definition
  328. boost::mysql::error_code boost::mysql::detail::deserialize_column_definition(
  329. span<const std::uint8_t> input,
  330. coldef_view& output
  331. )
  332. {
  333. deserialization_context ctx(input);
  334. struct column_definition_packet
  335. {
  336. string_lenenc catalog; // always "def"
  337. string_lenenc schema; // database
  338. string_lenenc table; // virtual table
  339. string_lenenc org_table; // physical table
  340. string_lenenc name; // virtual column name
  341. string_lenenc org_name; // physical column name
  342. string_lenenc fixed_fields;
  343. } pack{};
  344. // pack.fixed_fields itself is a structure like this.
  345. // The proto allows for extensibility here - adding fields just increasing fixed_fields.length
  346. struct fixed_fields_pack
  347. {
  348. int2 character_set; // collation id, somehow named character_set in the protocol docs
  349. int4 column_length; // maximum length of the field
  350. int1 type; // type of the column as defined in enum_field_types - this is a protocol_field_type
  351. int2 flags; // Flags as defined in Column Definition Flags
  352. int1 decimals; // max shown decimal digits. 0x00 for int/static strings; 0x1f for
  353. // dynamic strings, double, float
  354. } fixed_fields{};
  355. // Deserialize the main structure
  356. auto err = ctx.deserialize(
  357. pack.catalog,
  358. pack.schema,
  359. pack.table,
  360. pack.org_table,
  361. pack.name,
  362. pack.org_name,
  363. pack.fixed_fields
  364. );
  365. if (err != deserialize_errc::ok)
  366. return to_error_code(err);
  367. // Deserialize the fixed_fields structure.
  368. // Intentionally not checking for extra bytes here, since there may be unknown fields that should just get
  369. // ignored
  370. deserialization_context subctx(to_span(pack.fixed_fields.value));
  371. err = subctx.deserialize(
  372. fixed_fields.character_set,
  373. fixed_fields.column_length,
  374. fixed_fields.type,
  375. fixed_fields.flags,
  376. fixed_fields.decimals
  377. );
  378. if (err != deserialize_errc::ok)
  379. return to_error_code(err);
  380. // Compose output
  381. output = coldef_view{
  382. pack.schema.value,
  383. pack.table.value,
  384. pack.org_table.value,
  385. pack.name.value,
  386. pack.org_name.value,
  387. fixed_fields.character_set.value,
  388. fixed_fields.column_length.value,
  389. compute_column_type(
  390. static_cast<protocol_field_type>(fixed_fields.type.value),
  391. fixed_fields.flags.value,
  392. fixed_fields.character_set.value
  393. ),
  394. fixed_fields.flags.value,
  395. fixed_fields.decimals.value,
  396. };
  397. return ctx.check_extra_bytes();
  398. }
  399. boost::mysql::error_code boost::mysql::detail::deserialize_ok_response(
  400. span<const std::uint8_t> message,
  401. db_flavor flavor,
  402. diagnostics& diag,
  403. bool& backslash_escapes
  404. )
  405. {
  406. // Header
  407. int1 header{};
  408. deserialization_context ctx(message);
  409. auto err = to_error_code(header.deserialize(ctx));
  410. if (err)
  411. return err;
  412. if (header.value == ok_packet_header)
  413. {
  414. // Verify that the ok_packet is correct
  415. ok_view ok{};
  416. err = deserialize_ok_packet(ctx.to_span(), ok);
  417. if (err)
  418. return err;
  419. backslash_escapes = ok.backslash_escapes();
  420. return error_code();
  421. }
  422. else if (header.value == error_packet_header)
  423. {
  424. // Theoretically, the server can answer with an error packet, too
  425. return process_error_packet(ctx.to_span(), flavor, diag);
  426. }
  427. else
  428. {
  429. // Invalid message
  430. return client_errc::protocol_value_error;
  431. }
  432. }
  433. boost::mysql::error_code boost::mysql::detail::deserialize_prepare_stmt_response_impl(
  434. span<const std::uint8_t> message,
  435. prepare_stmt_response& output
  436. )
  437. {
  438. struct com_stmt_prepare_ok_packet
  439. {
  440. // std::uint8_t status: must be 0
  441. int4 statement_id;
  442. int2 num_columns;
  443. int2 num_params;
  444. int1 reserved_1; // must be 0
  445. int2 warning_count;
  446. // int1 metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA: not implemented
  447. } pack{};
  448. deserialization_context ctx(message);
  449. auto err = ctx.deserialize(
  450. pack.statement_id,
  451. pack.num_columns,
  452. pack.num_params,
  453. pack.reserved_1,
  454. pack.warning_count
  455. );
  456. if (err != deserialize_errc::ok)
  457. return to_error_code(err);
  458. output = prepare_stmt_response{
  459. pack.statement_id.value,
  460. pack.num_columns.value,
  461. pack.num_params.value,
  462. };
  463. return ctx.check_extra_bytes();
  464. }
  465. boost::mysql::error_code boost::mysql::detail::deserialize_prepare_stmt_response(
  466. span<const std::uint8_t> message,
  467. db_flavor flavor,
  468. prepare_stmt_response& output,
  469. diagnostics& diag
  470. )
  471. {
  472. deserialization_context ctx(message);
  473. int1 msg_type{};
  474. auto err = to_error_code(msg_type.deserialize(ctx));
  475. if (err)
  476. return err;
  477. if (msg_type.value == error_packet_header)
  478. {
  479. return process_error_packet(ctx.to_span(), flavor, diag);
  480. }
  481. else if (msg_type.value != 0)
  482. {
  483. return client_errc::protocol_value_error;
  484. }
  485. else
  486. {
  487. return deserialize_prepare_stmt_response_impl(ctx.to_span(), output);
  488. }
  489. }
  490. // execute response
  491. boost::mysql::detail::execute_response boost::mysql::detail::deserialize_execute_response(
  492. span<const std::uint8_t> msg,
  493. db_flavor flavor,
  494. diagnostics& diag
  495. )
  496. {
  497. // Response may be: ok_packet, err_packet, local infile request (not implemented)
  498. // If it is none of this, then the message type itself is the beginning of
  499. // a length-encoded int containing the field count
  500. deserialization_context ctx(msg);
  501. int1 msg_type{};
  502. auto err = to_error_code(msg_type.deserialize(ctx));
  503. if (err)
  504. return err;
  505. if (msg_type.value == ok_packet_header)
  506. {
  507. ok_view ok{};
  508. err = deserialize_ok_packet(ctx.to_span(), ok);
  509. if (err)
  510. return err;
  511. return ok;
  512. }
  513. else if (msg_type.value == error_packet_header)
  514. {
  515. return process_error_packet(ctx.to_span(), flavor, diag);
  516. }
  517. else
  518. {
  519. // Resultset with metadata. First packet is an int_lenenc with
  520. // the number of field definitions to expect. Message type is part
  521. // of this packet, so we must rewind the context
  522. ctx.rewind(1);
  523. int_lenenc num_fields{};
  524. err = to_error_code(num_fields.deserialize(ctx));
  525. if (err)
  526. return err;
  527. err = ctx.check_extra_bytes();
  528. if (err)
  529. return err;
  530. // We should have at least one field.
  531. // The max number of fields is some value around 1024. For simplicity/extensibility,
  532. // we accept anything less than 0xffff
  533. if (num_fields.value == 0 || num_fields.value > 0xffffu)
  534. {
  535. return make_error_code(client_errc::protocol_value_error);
  536. }
  537. return static_cast<std::size_t>(num_fields.value);
  538. }
  539. }
  540. boost::mysql::detail::row_message boost::mysql::detail::deserialize_row_message(
  541. span<const std::uint8_t> msg,
  542. db_flavor flavor,
  543. diagnostics& diag
  544. )
  545. {
  546. constexpr std::uint8_t eof_packet_header = 0xfe;
  547. // Message type: row, error or eof?
  548. int1 msg_type{};
  549. deserialization_context ctx(msg);
  550. auto deser_errc = msg_type.deserialize(ctx);
  551. if (deser_errc != deserialize_errc::ok)
  552. {
  553. return to_error_code(deser_errc);
  554. }
  555. if (msg_type.value == eof_packet_header)
  556. {
  557. // end of resultset => this is a ok_packet, not a row
  558. ok_view ok{};
  559. auto err = deserialize_ok_packet(ctx.to_span(), ok);
  560. if (err)
  561. return err;
  562. return ok;
  563. }
  564. else if (msg_type.value == error_packet_header)
  565. {
  566. // An error occurred during the generation of the rows
  567. return process_error_packet(ctx.to_span(), flavor, diag);
  568. }
  569. else
  570. {
  571. // An actual row
  572. ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
  573. return span<const std::uint8_t>(ctx.first(), ctx.size());
  574. }
  575. }
  576. // Deserialize row
  577. namespace boost {
  578. namespace mysql {
  579. namespace detail {
  580. inline bool is_next_field_null(const deserialization_context& ctx)
  581. {
  582. if (!ctx.enough_size(1))
  583. return false;
  584. return *ctx.first() == 0xfb;
  585. }
  586. inline error_code deserialize_text_row(
  587. deserialization_context& ctx,
  588. metadata_collection_view meta,
  589. field_view* output
  590. )
  591. {
  592. for (std::vector<field_view>::size_type i = 0; i < meta.size(); ++i)
  593. {
  594. if (is_next_field_null(ctx))
  595. {
  596. ctx.advance(1);
  597. output[i] = field_view(nullptr);
  598. }
  599. else
  600. {
  601. string_lenenc value_str;
  602. auto err = value_str.deserialize(ctx);
  603. if (err != deserialize_errc::ok)
  604. return to_error_code(err);
  605. err = deserialize_text_field(value_str.value, meta[i], output[i]);
  606. if (err != deserialize_errc::ok)
  607. return to_error_code(err);
  608. }
  609. }
  610. return ctx.check_extra_bytes();
  611. }
  612. inline error_code deserialize_binary_row(
  613. deserialization_context& ctx,
  614. metadata_collection_view meta,
  615. field_view* output
  616. )
  617. {
  618. // Skip packet header (it is not part of the message in the binary
  619. // protocol but it is in the text protocol, so we include it for homogeneity)
  620. if (!ctx.enough_size(1))
  621. return client_errc::incomplete_message;
  622. ctx.advance(1);
  623. // Number of fields
  624. std::size_t num_fields = meta.size();
  625. // Null bitmap
  626. null_bitmap_parser null_bitmap(num_fields);
  627. const std::uint8_t* null_bitmap_first = ctx.first();
  628. std::size_t null_bitmap_size = null_bitmap.byte_count();
  629. if (!ctx.enough_size(null_bitmap_size))
  630. return client_errc::incomplete_message;
  631. ctx.advance(null_bitmap_size);
  632. // Actual values
  633. for (std::vector<field_view>::size_type i = 0; i < num_fields; ++i)
  634. {
  635. if (null_bitmap.is_null(null_bitmap_first, i))
  636. {
  637. output[i] = field_view(nullptr);
  638. }
  639. else
  640. {
  641. auto err = deserialize_binary_field(ctx, meta[i], output[i]);
  642. if (err != deserialize_errc::ok)
  643. return to_error_code(err);
  644. }
  645. }
  646. // Check for remaining bytes
  647. return ctx.check_extra_bytes();
  648. }
  649. } // namespace detail
  650. } // namespace mysql
  651. } // namespace boost
  652. boost::mysql::error_code boost::mysql::detail::deserialize_row(
  653. resultset_encoding encoding,
  654. span<const std::uint8_t> buff,
  655. metadata_collection_view meta,
  656. span<field_view> output
  657. )
  658. {
  659. BOOST_ASSERT(meta.size() == output.size());
  660. deserialization_context ctx(buff);
  661. return encoding == detail::resultset_encoding::text ? deserialize_text_row(ctx, meta, output.data())
  662. : deserialize_binary_row(ctx, meta, output.data());
  663. }
  664. // Server hello
  665. namespace boost {
  666. namespace mysql {
  667. namespace detail {
  668. inline capabilities compose_capabilities(string_fixed<2> low, string_fixed<2> high)
  669. {
  670. std::uint32_t res = 0;
  671. auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&res);
  672. memcpy(capabilities_begin, low.value.data(), 2);
  673. memcpy(capabilities_begin + 2, high.value.data(), 2);
  674. return static_cast<capabilities>(boost::endian::little_to_native(res));
  675. }
  676. inline db_flavor parse_db_version(string_view version_string)
  677. {
  678. return version_string.find("MariaDB") != string_view::npos ? db_flavor::mariadb : db_flavor::mysql;
  679. }
  680. } // namespace detail
  681. } // namespace mysql
  682. } // namespace boost
  683. boost::mysql::error_code boost::mysql::detail::deserialize_server_hello_impl(
  684. span<const std::uint8_t> msg,
  685. server_hello& output
  686. )
  687. {
  688. struct server_hello_packet
  689. {
  690. // int<1> protocol version Always 10
  691. string_null server_version;
  692. int4 connection_id;
  693. string_fixed<8> auth_plugin_data_part_1;
  694. int1 filler; // should be 0
  695. string_fixed<2> capability_flags_low;
  696. int1 character_set; // default server a_protocol_character_set, only the lower 8-bits
  697. int2 status_flags; // server_status_flags
  698. string_fixed<2> capability_flags_high;
  699. int1 auth_plugin_data_len;
  700. string_fixed<10> reserved;
  701. // auth plugin data, 2nd part. This has a weird representation that doesn't fit any defined type
  702. string_null auth_plugin_name;
  703. } pack{};
  704. deserialization_context ctx(msg);
  705. auto err = ctx.deserialize(
  706. pack.server_version,
  707. pack.connection_id,
  708. pack.auth_plugin_data_part_1,
  709. pack.filler,
  710. pack.capability_flags_low,
  711. pack.character_set,
  712. pack.status_flags,
  713. pack.capability_flags_high
  714. );
  715. if (err != deserialize_errc::ok)
  716. return to_error_code(err);
  717. // Compose capabilities
  718. auto cap = compose_capabilities(pack.capability_flags_low, pack.capability_flags_high);
  719. // Check minimum server capabilities to deserialize this frame
  720. if (!has_capabilities(cap, capabilities::plugin_auth))
  721. return client_errc::server_unsupported;
  722. // Deserialize next fields
  723. err = ctx.deserialize(pack.auth_plugin_data_len, pack.reserved);
  724. if (err != deserialize_errc::ok)
  725. return to_error_code(err);
  726. // Auth plugin data, second part
  727. auto auth2_length = static_cast<std::uint8_t>((std::max)(
  728. static_cast<std::size_t>(13u),
  729. static_cast<std::size_t>(pack.auth_plugin_data_len.value - pack.auth_plugin_data_part_1.value.size())
  730. ));
  731. const std::uint8_t* auth2_data = ctx.first();
  732. if (!ctx.enough_size(auth2_length))
  733. return client_errc::incomplete_message;
  734. ctx.advance(auth2_length);
  735. // Auth plugin name
  736. err = pack.auth_plugin_name.deserialize(ctx);
  737. if (err != deserialize_errc::ok)
  738. return to_error_code(err);
  739. // Compose output
  740. output.connection_id = pack.connection_id.value;
  741. output.server = parse_db_version(pack.server_version.value);
  742. output.server_capabilities = cap;
  743. output.auth_plugin_name = pack.auth_plugin_name.value;
  744. // Compose auth_plugin_data
  745. output.auth_plugin_data.clear();
  746. output.auth_plugin_data.append(span<const std::uint8_t>(
  747. reinterpret_cast<const std::uint8_t*>(pack.auth_plugin_data_part_1.value.data()),
  748. pack.auth_plugin_data_part_1.value.size()
  749. ));
  750. // discard an extra trailing NULL byte
  751. output.auth_plugin_data.append(span<const std::uint8_t>(auth2_data, auth2_length - 1));
  752. return ctx.check_extra_bytes();
  753. }
  754. boost::mysql::error_code boost::mysql::detail::deserialize_server_hello(
  755. span<const std::uint8_t> msg,
  756. server_hello& output,
  757. diagnostics& diag
  758. )
  759. {
  760. constexpr std::uint8_t handshake_protocol_version_9 = 9;
  761. constexpr std::uint8_t handshake_protocol_version_10 = 10;
  762. deserialization_context ctx(msg);
  763. // Message type
  764. int1 msg_type{};
  765. auto err = to_error_code(msg_type.deserialize(ctx));
  766. if (err)
  767. return err;
  768. if (msg_type.value == handshake_protocol_version_9)
  769. {
  770. return make_error_code(client_errc::server_unsupported);
  771. }
  772. else if (msg_type.value == error_packet_header)
  773. {
  774. // We don't know which DB is yet. The server has no knowledge of our capabilities
  775. // yet, so it will assume we don't support the 4.1 protocol and send an error
  776. // packet without SQL state
  777. return process_error_packet(ctx.to_span(), db_flavor::mysql, diag, false);
  778. }
  779. else if (msg_type.value != handshake_protocol_version_10)
  780. {
  781. return make_error_code(client_errc::protocol_value_error);
  782. }
  783. else
  784. {
  785. return deserialize_server_hello_impl(ctx.to_span(), output);
  786. }
  787. }
  788. // auth_switch
  789. BOOST_ATTRIBUTE_NODISCARD
  790. boost::mysql::error_code boost::mysql::detail::deserialize_auth_switch(
  791. span<const std::uint8_t> msg,
  792. auth_switch& output
  793. )
  794. {
  795. struct auth_switch_request_packet
  796. {
  797. string_null plugin_name;
  798. string_eof auth_plugin_data;
  799. } pack{};
  800. deserialization_context ctx(msg);
  801. auto err = ctx.deserialize(pack.plugin_name, pack.auth_plugin_data);
  802. if (err != deserialize_errc::ok)
  803. return to_error_code(err);
  804. // Discard an additional NULL at the end of auth data
  805. string_view auth_data = pack.auth_plugin_data.value;
  806. if (!auth_data.empty() && auth_data.back() == 0)
  807. {
  808. auth_data = auth_data.substr(0, auth_data.size() - 1);
  809. }
  810. output = {
  811. pack.plugin_name.value,
  812. to_span(auth_data),
  813. };
  814. return ctx.check_extra_bytes();
  815. }
  816. boost::mysql::detail::handshake_server_response boost::mysql::detail::deserialize_handshake_server_response(
  817. span<const std::uint8_t> buff,
  818. db_flavor flavor,
  819. diagnostics& diag
  820. )
  821. {
  822. constexpr std::uint8_t auth_switch_request_header = 0xfe;
  823. constexpr std::uint8_t auth_more_data_header = 0x01;
  824. deserialization_context ctx(buff);
  825. int1 msg_type{};
  826. auto err = to_error_code(msg_type.deserialize(ctx));
  827. if (err)
  828. return err;
  829. if (msg_type.value == ok_packet_header)
  830. {
  831. ok_view ok{};
  832. err = deserialize_ok_packet(ctx.to_span(), ok);
  833. if (err)
  834. return err;
  835. return ok;
  836. }
  837. else if (msg_type.value == error_packet_header)
  838. {
  839. return process_error_packet(ctx.to_span(), flavor, diag);
  840. }
  841. else if (msg_type.value == auth_switch_request_header)
  842. {
  843. // We have received an auth switch request. Deserialize it
  844. auth_switch auth_sw{};
  845. err = deserialize_auth_switch(ctx.to_span(), auth_sw);
  846. if (err)
  847. return err;
  848. return auth_sw;
  849. }
  850. else if (msg_type.value == auth_more_data_header)
  851. {
  852. // We have received an auth more data request. Deserialize it.
  853. // Note that string_eof never fails deserialization (by definition)
  854. string_eof auth_more_data;
  855. auto ec = auth_more_data.deserialize(ctx);
  856. BOOST_ASSERT(ec == deserialize_errc::ok);
  857. boost::ignore_unused(ec);
  858. return handshake_server_response(to_span(auth_more_data.value));
  859. }
  860. else
  861. {
  862. // Unknown message type
  863. return make_error_code(client_errc::protocol_value_error);
  864. }
  865. }
  866. #endif