deserialization.hpp 30 KB

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