File indexing completed on 2025-01-18 09:42:41
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_PROTOCOL_IPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_PROTOCOL_IPP
0010
0011 #pragma once
0012
0013 #include <boost/mysql/client_errc.hpp>
0014 #include <boost/mysql/common_server_errc.hpp>
0015 #include <boost/mysql/error_categories.hpp>
0016 #include <boost/mysql/error_code.hpp>
0017 #include <boost/mysql/field_kind.hpp>
0018 #include <boost/mysql/string_view.hpp>
0019
0020 #include <boost/mysql/detail/config.hpp>
0021
0022 #include <boost/mysql/impl/internal/error/server_error_to_string.hpp>
0023 #include <boost/mysql/impl/internal/make_string_view.hpp>
0024 #include <boost/mysql/impl/internal/protocol/basic_types.hpp>
0025 #include <boost/mysql/impl/internal/protocol/binary_serialization.hpp>
0026 #include <boost/mysql/impl/internal/protocol/capabilities.hpp>
0027 #include <boost/mysql/impl/internal/protocol/constants.hpp>
0028 #include <boost/mysql/impl/internal/protocol/deserialize_binary_field.hpp>
0029 #include <boost/mysql/impl/internal/protocol/deserialize_text_field.hpp>
0030 #include <boost/mysql/impl/internal/protocol/null_bitmap_traits.hpp>
0031 #include <boost/mysql/impl/internal/protocol/protocol.hpp>
0032 #include <boost/mysql/impl/internal/protocol/serialization.hpp>
0033
0034 #include <boost/core/ignore_unused.hpp>
0035 #include <boost/core/span.hpp>
0036
0037 #include <cstddef>
0038
0039 namespace boost {
0040 namespace mysql {
0041 namespace detail {
0042
0043
0044 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t handshake_protocol_version_9 = 9;
0045 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t handshake_protocol_version_10 = 10;
0046 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t error_packet_header = 0xff;
0047 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t ok_packet_header = 0x00;
0048 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t eof_packet_header = 0xfe;
0049 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t auth_switch_request_header = 0xfe;
0050 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::uint8_t auth_more_data_header = 0x01;
0051 BOOST_MYSQL_STATIC_IF_COMPILED constexpr string_view fast_auth_complete_challenge = make_string_view("\3");
0052
0053
0054 BOOST_MYSQL_STATIC_OR_INLINE
0055 void serialize_command_id(span<std::uint8_t> buff, std::uint8_t command_id) noexcept
0056 {
0057 BOOST_ASSERT(buff.size() >= 1u);
0058 buff[0] = command_id;
0059 }
0060
0061 struct frame_header_packet
0062 {
0063 int3 packet_size;
0064 std::uint8_t sequence_number;
0065 };
0066
0067
0068 static protocol_field_type get_protocol_field_type(field_view input) noexcept
0069 {
0070 switch (input.kind())
0071 {
0072 case field_kind::null: return protocol_field_type::null;
0073 case field_kind::int64: return protocol_field_type::longlong;
0074 case field_kind::uint64: return protocol_field_type::longlong;
0075 case field_kind::string: return protocol_field_type::string;
0076 case field_kind::blob: return protocol_field_type::blob;
0077 case field_kind::float_: return protocol_field_type::float_;
0078 case field_kind::double_: return protocol_field_type::double_;
0079 case field_kind::date: return protocol_field_type::date;
0080 case field_kind::datetime: return protocol_field_type::datetime;
0081 case field_kind::time: return protocol_field_type::time;
0082 default: BOOST_ASSERT(false); return protocol_field_type::null;
0083 }
0084 }
0085
0086 }
0087 }
0088 }
0089
0090
0091 void boost::mysql::detail::serialize_frame_header(
0092 frame_header msg,
0093 span<std::uint8_t, frame_header_size> buffer
0094 ) noexcept
0095 {
0096 BOOST_ASSERT(msg.size <= 0xffffff);
0097 serialization_context ctx(buffer.data());
0098 frame_header_packet pack{int3{msg.size}, msg.sequence_number};
0099 serialize(ctx, pack.packet_size, pack.sequence_number);
0100 }
0101
0102 boost::mysql::detail::frame_header boost::mysql::detail::deserialize_frame_header(
0103 span<const std::uint8_t, frame_header_size> buffer
0104 ) noexcept
0105 {
0106 frame_header_packet pack{};
0107 deserialization_context ctx(buffer.data(), buffer.size());
0108 auto err = deserialize(ctx, pack.packet_size, pack.sequence_number);
0109 BOOST_ASSERT(err == deserialize_errc::ok);
0110 boost::ignore_unused(err);
0111 return frame_header{pack.packet_size.value, pack.sequence_number};
0112 }
0113
0114
0115 boost::mysql::error_code boost::mysql::detail::deserialize_ok_packet(
0116 span<const std::uint8_t> msg,
0117 ok_view& output
0118 ) noexcept
0119 {
0120 struct ok_packet
0121 {
0122
0123 int_lenenc affected_rows;
0124 int_lenenc last_insert_id;
0125 std::uint16_t status_flags;
0126 std::uint16_t warnings;
0127
0128 string_lenenc info;
0129 } pack{};
0130
0131 deserialization_context ctx(msg);
0132 auto err = deserialize(ctx, pack.affected_rows, pack.last_insert_id, pack.status_flags, pack.warnings);
0133 if (err != deserialize_errc::ok)
0134 return to_error_code(err);
0135
0136 if (ctx.enough_size(1))
0137 {
0138 err = deserialize(ctx, pack.info);
0139 if (err != deserialize_errc::ok)
0140 return to_error_code(err);
0141 }
0142
0143 output = {
0144 pack.affected_rows.value,
0145 pack.last_insert_id.value,
0146 pack.status_flags,
0147 pack.warnings,
0148 pack.info.value,
0149 };
0150
0151 return ctx.check_extra_bytes();
0152 }
0153
0154
0155 boost::mysql::error_code boost::mysql::detail::deserialize_error_packet(
0156 span<const std::uint8_t> msg,
0157 err_view& output
0158 ) noexcept
0159 {
0160 struct err_packet
0161 {
0162
0163 std::uint16_t error_code;
0164 string_fixed<1> sql_state_marker;
0165 string_fixed<5> sql_state;
0166 string_eof error_message;
0167 } pack{};
0168
0169 deserialization_context ctx(msg);
0170 auto err = deserialize(ctx, pack.error_code, pack.sql_state_marker, pack.sql_state, pack.error_message);
0171 if (err != deserialize_errc::ok)
0172 return to_error_code(err);
0173
0174 output = err_view{
0175 pack.error_code,
0176 pack.error_message.value,
0177 };
0178
0179 return ctx.check_extra_bytes();
0180 }
0181
0182 boost::mysql::error_code boost::mysql::detail::process_error_packet(
0183 span<const std::uint8_t> msg,
0184 db_flavor flavor,
0185 diagnostics& diag
0186 )
0187 {
0188 err_view error_packet{};
0189 auto err = deserialize_error_packet(msg, error_packet);
0190 if (err)
0191 return err;
0192
0193
0194 access::get_impl(diag).assign_server(error_packet.error_message);
0195
0196
0197 if (common_error_to_string(error_packet.error_code))
0198 {
0199
0200
0201
0202 return static_cast<common_server_errc>(error_packet.error_code);
0203 }
0204 else
0205 {
0206
0207
0208 const auto& cat = flavor == db_flavor::mysql ? get_mysql_server_category()
0209 : get_mariadb_server_category();
0210 return error_code(error_packet.error_code, cat);
0211 }
0212 }
0213
0214
0215 boost::mysql::error_code boost::mysql::detail::deserialize_column_definition(
0216 span<const std::uint8_t> input,
0217 coldef_view& output
0218 ) noexcept
0219 {
0220 deserialization_context ctx(input);
0221
0222 struct column_definition_packet
0223 {
0224 string_lenenc catalog;
0225 string_lenenc schema;
0226 string_lenenc table;
0227 string_lenenc org_table;
0228 string_lenenc name;
0229 string_lenenc org_name;
0230 string_lenenc fixed_fields;
0231 } pack{};
0232
0233
0234
0235 struct fixed_fields_pack
0236 {
0237 std::uint16_t character_set;
0238 std::uint32_t column_length;
0239 protocol_field_type type;
0240 std::uint16_t flags;
0241 std::uint8_t decimals;
0242
0243 } fixed_fields{};
0244
0245
0246 auto err = deserialize(
0247 ctx,
0248 pack.catalog,
0249 pack.schema,
0250 pack.table,
0251 pack.org_table,
0252 pack.name,
0253 pack.org_name,
0254 pack.fixed_fields
0255 );
0256 if (err != deserialize_errc::ok)
0257 return to_error_code(err);
0258
0259
0260
0261
0262 deserialization_context subctx(
0263 reinterpret_cast<const std::uint8_t*>(pack.fixed_fields.value.data()),
0264 pack.fixed_fields.value.size()
0265 );
0266 err = deserialize(
0267 subctx,
0268 fixed_fields.character_set,
0269 fixed_fields.column_length,
0270 fixed_fields.type,
0271 fixed_fields.flags,
0272 fixed_fields.decimals
0273 );
0274 if (err != deserialize_errc::ok)
0275 return to_error_code(err);
0276
0277
0278 output = coldef_view{
0279 pack.schema.value,
0280 pack.table.value,
0281 pack.org_table.value,
0282 pack.name.value,
0283 pack.org_name.value,
0284 fixed_fields.character_set,
0285 fixed_fields.column_length,
0286 compute_column_type(fixed_fields.type, fixed_fields.flags, fixed_fields.character_set),
0287 fixed_fields.flags,
0288 fixed_fields.decimals,
0289 };
0290
0291 return ctx.check_extra_bytes();
0292 }
0293
0294
0295 std::size_t boost::mysql::detail::quit_command::get_size() const noexcept { return 1u; }
0296 void boost::mysql::detail::quit_command::serialize(span<std::uint8_t> buff) const noexcept
0297 {
0298 serialize_command_id(buff, 0x01);
0299 }
0300
0301
0302 std::size_t boost::mysql::detail::ping_command::get_size() const noexcept { return 1u; }
0303 void boost::mysql::detail::ping_command::serialize(span<std::uint8_t> buff) const noexcept
0304 {
0305 serialize_command_id(buff, 0x0e);
0306 }
0307
0308
0309 std::size_t boost::mysql::detail::reset_connection_command::get_size() const noexcept { return 1u; }
0310 void boost::mysql::detail::reset_connection_command::serialize(span<std::uint8_t> buff) const noexcept
0311 {
0312 serialize_command_id(buff, 0x1f);
0313 }
0314
0315 boost::mysql::error_code boost::mysql::detail::deserialize_ok_response(
0316 span<const std::uint8_t> message,
0317 db_flavor flavor,
0318 diagnostics& diag
0319 )
0320 {
0321
0322 std::uint8_t header{};
0323 deserialization_context ctx(message);
0324 auto err = to_error_code(deserialize(ctx, header));
0325 if (err)
0326 return err;
0327
0328 if (header == ok_packet_header)
0329 {
0330
0331 ok_view ok{};
0332 return deserialize_ok_packet(ctx.to_span(), ok);
0333 }
0334 else if (header == error_packet_header)
0335 {
0336
0337 return process_error_packet(ctx.to_span(), flavor, diag);
0338 }
0339 else
0340 {
0341
0342 return client_errc::protocol_value_error;
0343 }
0344 }
0345
0346
0347 std::size_t boost::mysql::detail::query_command::get_size() const noexcept
0348 {
0349 return ::boost::mysql::detail::get_size(string_eof{query}) + 1;
0350 }
0351 void boost::mysql::detail::query_command::serialize(span<std::uint8_t> buff) const noexcept
0352 {
0353 constexpr std::uint8_t command_id = 0x03;
0354
0355 BOOST_ASSERT(buff.size() >= get_size());
0356 serialization_context ctx(buff.data());
0357 ::boost::mysql::detail::serialize(ctx, command_id, string_eof{query});
0358 }
0359
0360
0361 std::size_t boost::mysql::detail::prepare_stmt_command::get_size() const noexcept
0362 {
0363 return ::boost::mysql::detail::get_size(string_eof{stmt}) + 1;
0364 }
0365 void boost::mysql::detail::prepare_stmt_command::serialize(span<std::uint8_t> buff) const noexcept
0366 {
0367 constexpr std::uint8_t command_id = 0x16;
0368
0369 BOOST_ASSERT(buff.size() >= get_size());
0370 serialization_context ctx(buff.data());
0371 ::boost::mysql::detail::serialize(ctx, command_id, string_eof{stmt});
0372 }
0373
0374 boost::mysql::error_code boost::mysql::detail::deserialize_prepare_stmt_response_impl(
0375 span<const std::uint8_t> message,
0376 prepare_stmt_response& output
0377 ) noexcept
0378 {
0379 struct com_stmt_prepare_ok_packet
0380 {
0381
0382 std::uint32_t statement_id;
0383 std::uint16_t num_columns;
0384 std::uint16_t num_params;
0385 std::uint8_t reserved_1;
0386 std::uint16_t warning_count;
0387
0388 } pack{};
0389
0390 deserialization_context ctx(message);
0391
0392 auto err = deserialize(
0393 ctx,
0394 pack.statement_id,
0395 pack.num_columns,
0396 pack.num_params,
0397 pack.reserved_1,
0398 pack.warning_count
0399 );
0400 if (err != deserialize_errc::ok)
0401 return to_error_code(err);
0402
0403 output = prepare_stmt_response{
0404 pack.statement_id,
0405 pack.num_columns,
0406 pack.num_params,
0407 };
0408
0409 return ctx.check_extra_bytes();
0410 }
0411
0412 boost::mysql::error_code boost::mysql::detail::deserialize_prepare_stmt_response(
0413 span<const std::uint8_t> message,
0414 db_flavor flavor,
0415 prepare_stmt_response& output,
0416 diagnostics& diag
0417 )
0418 {
0419 deserialization_context ctx(message);
0420 std::uint8_t msg_type = 0;
0421 auto err = to_error_code(deserialize(ctx, msg_type));
0422 if (err)
0423 return err;
0424
0425 if (msg_type == error_packet_header)
0426 {
0427 return process_error_packet(ctx.to_span(), flavor, diag);
0428 }
0429 else if (msg_type != 0)
0430 {
0431 return client_errc::protocol_value_error;
0432 }
0433 else
0434 {
0435 return deserialize_prepare_stmt_response_impl(ctx.to_span(), output);
0436 }
0437 }
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452 std::size_t boost::mysql::detail::execute_stmt_command::get_size() const noexcept
0453 {
0454 constexpr std::size_t param_meta_packet_size = 2;
0455 constexpr std::size_t stmt_execute_packet_head_size = 1
0456 + 4
0457 + 1
0458 + 4;
0459 std::size_t res = stmt_execute_packet_head_size;
0460 auto num_params = params.size();
0461 if (num_params > 0u)
0462 {
0463 res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
0464 res += 1;
0465 res += param_meta_packet_size * num_params;
0466 for (field_view param : params)
0467 {
0468 res += ::boost::mysql::detail::get_size(param);
0469 }
0470 }
0471
0472 return res;
0473 }
0474
0475 void boost::mysql::detail::execute_stmt_command::serialize(span<std::uint8_t> buff) const noexcept
0476 {
0477 constexpr std::uint8_t command_id = 0x17;
0478
0479 serialization_context ctx(buff.data());
0480 BOOST_ASSERT(buff.size() >= get_size());
0481
0482 std::uint32_t statement_id = this->statement_id;
0483 std::uint8_t flags = 0;
0484 std::uint32_t iteration_count = 1;
0485 std::uint8_t new_params_bind_flag = 1;
0486
0487 ::boost::mysql::detail::serialize(ctx, command_id, statement_id, flags, iteration_count);
0488
0489
0490 auto num_params = params.size();
0491
0492 if (num_params > 0)
0493 {
0494
0495 null_bitmap_traits traits(stmt_execute_null_bitmap_offset, num_params);
0496 std::memset(ctx.first(), 0, traits.byte_count());
0497 for (std::size_t i = 0; i < num_params; ++i)
0498 {
0499 if (params[i].is_null())
0500 {
0501 traits.set_null(ctx.first(), i);
0502 }
0503 }
0504 ctx.advance(traits.byte_count());
0505
0506
0507 ::boost::mysql::detail::serialize(ctx, new_params_bind_flag);
0508
0509
0510 for (field_view param : params)
0511 {
0512 protocol_field_type type = get_protocol_field_type(param);
0513 std::uint8_t unsigned_flag = param.is_uint64() ? std::uint8_t(0x80) : std::uint8_t(0);
0514 ::boost::mysql::detail::serialize(ctx, type, unsigned_flag);
0515 }
0516
0517
0518 for (field_view param : params)
0519 {
0520 ::boost::mysql::detail::serialize(ctx, param);
0521 }
0522 }
0523 }
0524
0525
0526 std::size_t boost::mysql::detail::close_stmt_command::get_size() const noexcept { return 5u; }
0527
0528 void boost::mysql::detail::close_stmt_command::serialize(span<std::uint8_t> buff) const noexcept
0529 {
0530 constexpr std::uint8_t command_id = 0x19;
0531
0532 serialization_context ctx(buff.data());
0533 BOOST_ASSERT(buff.size() >= get_size());
0534
0535 ::boost::mysql::detail::serialize(ctx, command_id, statement_id);
0536 }
0537
0538
0539 boost::mysql::detail::execute_response boost::mysql::detail::deserialize_execute_response(
0540 span<const std::uint8_t> msg,
0541 db_flavor flavor,
0542 diagnostics& diag
0543 ) noexcept
0544 {
0545
0546
0547
0548 deserialization_context ctx(msg);
0549 std::uint8_t msg_type = 0;
0550 auto err = to_error_code(deserialize(ctx, msg_type));
0551 if (err)
0552 return err;
0553
0554 if (msg_type == ok_packet_header)
0555 {
0556 ok_view ok{};
0557 err = deserialize_ok_packet(ctx.to_span(), ok);
0558 if (err)
0559 return err;
0560 return ok;
0561 }
0562 else if (msg_type == error_packet_header)
0563 {
0564 return process_error_packet(ctx.to_span(), flavor, diag);
0565 }
0566 else
0567 {
0568
0569
0570
0571 ctx.rewind(1);
0572 int_lenenc num_fields;
0573 err = to_error_code(deserialize(ctx, num_fields));
0574 if (err)
0575 return err;
0576 err = ctx.check_extra_bytes();
0577 if (err)
0578 return err;
0579
0580
0581
0582
0583 if (num_fields.value == 0 || num_fields.value > 0xffffu)
0584 {
0585 return make_error_code(client_errc::protocol_value_error);
0586 }
0587
0588 return static_cast<std::size_t>(num_fields.value);
0589 }
0590 }
0591
0592 boost::mysql::detail::row_message boost::mysql::detail::deserialize_row_message(
0593 span<const std::uint8_t> msg,
0594 db_flavor flavor,
0595 diagnostics& diag
0596 )
0597 {
0598
0599 std::uint8_t msg_type = 0;
0600 deserialization_context ctx(msg);
0601 auto deser_errc = deserialize(ctx, msg_type);
0602 if (deser_errc != deserialize_errc::ok)
0603 {
0604 return to_error_code(deser_errc);
0605 }
0606
0607 if (msg_type == eof_packet_header)
0608 {
0609
0610 ok_view ok{};
0611 auto err = deserialize_ok_packet(ctx.to_span(), ok);
0612 if (err)
0613 return err;
0614 return ok;
0615 }
0616 else if (msg_type == error_packet_header)
0617 {
0618
0619 return process_error_packet(ctx.to_span(), flavor, diag);
0620 }
0621 else
0622 {
0623
0624 ctx.rewind(1);
0625 return span<const std::uint8_t>(ctx.first(), ctx.size());
0626 }
0627 }
0628
0629
0630 namespace boost {
0631 namespace mysql {
0632 namespace detail {
0633
0634 BOOST_MYSQL_STATIC_OR_INLINE
0635 bool is_next_field_null(const deserialization_context& ctx) noexcept
0636 {
0637 if (!ctx.enough_size(1))
0638 return false;
0639 return *ctx.first() == 0xfb;
0640 }
0641
0642 BOOST_MYSQL_STATIC_OR_INLINE
0643 error_code deserialize_text_row(
0644 deserialization_context& ctx,
0645 metadata_collection_view meta,
0646 field_view* output
0647 )
0648 {
0649 for (std::vector<field_view>::size_type i = 0; i < meta.size(); ++i)
0650 {
0651 if (is_next_field_null(ctx))
0652 {
0653 ctx.advance(1);
0654 output[i] = field_view(nullptr);
0655 }
0656 else
0657 {
0658 string_lenenc value_str;
0659 auto err = deserialize(ctx, value_str);
0660 if (err != deserialize_errc::ok)
0661 return to_error_code(err);
0662 err = deserialize_text_field(value_str.value, meta[i], output[i]);
0663 if (err != deserialize_errc::ok)
0664 return to_error_code(err);
0665 }
0666 }
0667 if (!ctx.empty())
0668 return client_errc::extra_bytes;
0669 return error_code();
0670 }
0671
0672 BOOST_MYSQL_STATIC_OR_INLINE
0673 error_code deserialize_binary_row(
0674 deserialization_context& ctx,
0675 metadata_collection_view meta,
0676 field_view* output
0677 )
0678 {
0679
0680
0681 if (!ctx.enough_size(1))
0682 return client_errc::incomplete_message;
0683 ctx.advance(1);
0684
0685
0686 std::size_t num_fields = meta.size();
0687
0688
0689 null_bitmap_traits null_bitmap(binary_row_null_bitmap_offset, num_fields);
0690 const std::uint8_t* null_bitmap_begin = ctx.first();
0691 if (!ctx.enough_size(null_bitmap.byte_count()))
0692 return client_errc::incomplete_message;
0693 ctx.advance(null_bitmap.byte_count());
0694
0695
0696 for (std::vector<field_view>::size_type i = 0; i < num_fields; ++i)
0697 {
0698 if (null_bitmap.is_null(null_bitmap_begin, i))
0699 {
0700 output[i] = field_view(nullptr);
0701 }
0702 else
0703 {
0704 auto err = deserialize_binary_field(ctx, meta[i], output[i]);
0705 if (err != deserialize_errc::ok)
0706 return to_error_code(err);
0707 }
0708 }
0709
0710
0711 if (!ctx.empty())
0712 return make_error_code(client_errc::extra_bytes);
0713
0714 return error_code();
0715 }
0716
0717 }
0718 }
0719 }
0720
0721 boost::mysql::error_code boost::mysql::detail::deserialize_row(
0722 resultset_encoding encoding,
0723 span<const std::uint8_t> buff,
0724 metadata_collection_view meta,
0725 span<field_view> output
0726 )
0727 {
0728 BOOST_ASSERT(meta.size() == output.size());
0729 deserialization_context ctx(buff);
0730 return encoding == detail::resultset_encoding::text ? deserialize_text_row(ctx, meta, output.data())
0731 : deserialize_binary_row(ctx, meta, output.data());
0732 }
0733
0734
0735 namespace boost {
0736 namespace mysql {
0737 namespace detail {
0738
0739 BOOST_MYSQL_STATIC_OR_INLINE
0740 capabilities compose_capabilities(string_fixed<2> low, string_fixed<2> high) noexcept
0741 {
0742 std::uint32_t res = 0;
0743 auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&res);
0744 memcpy(capabilities_begin, low.value.data(), 2);
0745 memcpy(capabilities_begin + 2, high.value.data(), 2);
0746 return capabilities(boost::endian::little_to_native(res));
0747 }
0748
0749 BOOST_MYSQL_STATIC_OR_INLINE
0750 db_flavor parse_db_version(string_view version_string) noexcept
0751 {
0752 return version_string.find("MariaDB") != string_view::npos ? db_flavor::mariadb : db_flavor::mysql;
0753 }
0754
0755 BOOST_MYSQL_STATIC_IF_COMPILED
0756 constexpr std::uint8_t server_hello_auth1_length = 8;
0757
0758 }
0759 }
0760 }
0761
0762 boost::mysql::error_code boost::mysql::detail::deserialize_server_hello_impl(
0763 span<const std::uint8_t> msg,
0764 server_hello& output
0765 )
0766 {
0767 struct server_hello_packet
0768 {
0769
0770 string_null server_version;
0771 std::uint32_t connection_id;
0772 string_fixed<server_hello_auth1_length> auth_plugin_data_part_1;
0773 std::uint8_t filler;
0774 string_fixed<2> capability_flags_low;
0775 std::uint8_t character_set;
0776 std::uint16_t status_flags;
0777 string_fixed<2> capability_flags_high;
0778 std::uint8_t auth_plugin_data_len;
0779 string_fixed<10> reserved;
0780
0781 string_null auth_plugin_name;
0782 } pack{};
0783
0784 deserialization_context ctx(msg);
0785
0786 auto err = deserialize(
0787 ctx,
0788 pack.server_version,
0789 pack.connection_id,
0790 pack.auth_plugin_data_part_1,
0791 pack.filler,
0792 pack.capability_flags_low,
0793 pack.character_set,
0794 pack.status_flags,
0795 pack.capability_flags_high
0796 );
0797 if (err != deserialize_errc::ok)
0798 return to_error_code(err);
0799
0800
0801 auto cap = compose_capabilities(pack.capability_flags_low, pack.capability_flags_high);
0802
0803
0804 if (!cap.has(CLIENT_PLUGIN_AUTH))
0805 return client_errc::server_unsupported;
0806
0807
0808 err = deserialize(ctx, pack.auth_plugin_data_len, pack.reserved);
0809 if (err != deserialize_errc::ok)
0810 return to_error_code(err);
0811
0812
0813 auto auth2_length = static_cast<std::uint8_t>(
0814 (std::max)(13, pack.auth_plugin_data_len - server_hello_auth1_length)
0815 );
0816 const void* auth2_data = ctx.first();
0817 if (!ctx.enough_size(auth2_length))
0818 return client_errc::incomplete_message;
0819 ctx.advance(auth2_length);
0820
0821
0822 err = deserialize(ctx, pack.auth_plugin_name);
0823 if (err != deserialize_errc::ok)
0824 return to_error_code(err);
0825
0826
0827 output.server = parse_db_version(pack.server_version.value);
0828 output.server_capabilities = cap;
0829 output.auth_plugin_name = pack.auth_plugin_name.value;
0830
0831
0832 output.auth_plugin_data.clear();
0833 output.auth_plugin_data.append(pack.auth_plugin_data_part_1.value.data(), server_hello_auth1_length);
0834 output.auth_plugin_data.append(auth2_data,
0835 auth2_length - 1);
0836
0837 return ctx.check_extra_bytes();
0838 }
0839
0840 boost::mysql::error_code boost::mysql::detail::deserialize_server_hello(
0841 span<const std::uint8_t> msg,
0842 server_hello& output,
0843 diagnostics& diag
0844 )
0845 {
0846 deserialization_context ctx(msg);
0847
0848
0849 std::uint8_t msg_type = 0;
0850 auto err = to_error_code(deserialize(ctx, msg_type));
0851 if (err)
0852 return err;
0853 if (msg_type == handshake_protocol_version_9)
0854 {
0855 return make_error_code(client_errc::server_unsupported);
0856 }
0857 else if (msg_type == error_packet_header)
0858 {
0859
0860 return process_error_packet(ctx.to_span(), db_flavor::mysql, diag);
0861 }
0862 else if (msg_type != handshake_protocol_version_10)
0863 {
0864 return make_error_code(client_errc::protocol_value_error);
0865 }
0866 else
0867 {
0868 return deserialize_server_hello_impl(ctx.to_span(), output);
0869 }
0870 }
0871
0872
0873 namespace boost {
0874 namespace mysql {
0875 namespace detail {
0876
0877 BOOST_MYSQL_STATIC_OR_INLINE
0878 std::uint8_t get_collation_first_byte(std::uint32_t collation_id) noexcept
0879 {
0880 return static_cast<std::uint8_t>(collation_id % 0xff);
0881 }
0882
0883 struct login_request_packet
0884 {
0885 std::uint32_t client_flag;
0886 std::uint32_t max_packet_size;
0887 std::uint8_t character_set;
0888 string_fixed<23> filler;
0889 string_null username;
0890 string_lenenc auth_response;
0891 string_null database;
0892 string_null client_plugin_name;
0893
0894 };
0895
0896 BOOST_MYSQL_STATIC_OR_INLINE
0897 login_request_packet to_packet(const login_request& req) noexcept
0898 {
0899 return {
0900 req.negotiated_capabilities.get(),
0901 req.max_packet_size,
0902 get_collation_first_byte(req.collation_id),
0903 {},
0904 string_null{req.username},
0905 string_lenenc{to_string(req.auth_response)},
0906 string_null{req.database},
0907 string_null{req.auth_plugin_name},
0908 };
0909 }
0910
0911 }
0912 }
0913 }
0914
0915 std::size_t boost::mysql::detail::login_request::get_size() const noexcept
0916 {
0917 auto pack = to_packet(*this);
0918 return ::boost::mysql::detail::get_size(
0919 pack.client_flag,
0920 pack.max_packet_size,
0921 pack.character_set,
0922 pack.filler,
0923 pack.username,
0924 pack.auth_response
0925 ) +
0926 (negotiated_capabilities.has(CLIENT_CONNECT_WITH_DB)
0927 ? ::boost::mysql::detail::get_size(pack.database)
0928 : 0) +
0929 ::boost::mysql::detail::get_size(pack.client_plugin_name);
0930 }
0931
0932 void boost::mysql::detail::login_request::serialize(span<std::uint8_t> buff) const noexcept
0933 {
0934 BOOST_ASSERT(buff.size() >= get_size());
0935 serialization_context ctx(buff.data());
0936
0937 auto pack = to_packet(*this);
0938 ::boost::mysql::detail::serialize(
0939 ctx,
0940 pack.client_flag,
0941 pack.max_packet_size,
0942 pack.character_set,
0943 pack.filler,
0944 pack.username,
0945 pack.auth_response
0946 );
0947 if (negotiated_capabilities.has(CLIENT_CONNECT_WITH_DB))
0948 {
0949 ::boost::mysql::detail::serialize(ctx, pack.database);
0950 }
0951 ::boost::mysql::detail::serialize(ctx, pack.client_plugin_name);
0952 }
0953
0954
0955 std::size_t boost::mysql::detail::ssl_request::get_size() const noexcept { return 4 + 4 + 1 + 23; }
0956
0957 void boost::mysql::detail::ssl_request::serialize(span<std::uint8_t> buff) const noexcept
0958 {
0959 BOOST_ASSERT(buff.size() >= get_size());
0960 serialization_context ctx(buff.data());
0961
0962 struct ssl_request_packet
0963 {
0964 std::uint32_t client_flag;
0965 std::uint32_t max_packet_size;
0966 std::uint8_t character_set;
0967 string_fixed<23> filler;
0968 } pack{
0969 negotiated_capabilities.get(),
0970 max_packet_size,
0971 get_collation_first_byte(collation_id),
0972 {},
0973 };
0974
0975 ::boost::mysql::detail::serialize(
0976 ctx,
0977 pack.client_flag,
0978 pack.max_packet_size,
0979 pack.character_set,
0980 pack.filler
0981 );
0982 }
0983
0984
0985 BOOST_ATTRIBUTE_NODISCARD
0986 boost::mysql::error_code boost::mysql::detail::deserialize_auth_switch(
0987 span<const std::uint8_t> msg,
0988 auth_switch& output
0989 ) noexcept
0990 {
0991 struct auth_switch_request_packet
0992 {
0993 string_null plugin_name;
0994 string_eof auth_plugin_data;
0995 } pack{};
0996
0997 deserialization_context ctx(msg);
0998
0999 auto err = deserialize(ctx, pack.plugin_name, pack.auth_plugin_data);
1000 if (err != deserialize_errc::ok)
1001 return to_error_code(err);
1002
1003
1004 string_view auth_data = pack.auth_plugin_data.value;
1005 if (!auth_data.empty() && auth_data.back() == 0)
1006 {
1007 auth_data = auth_data.substr(0, auth_data.size() - 1);
1008 }
1009
1010 output = {
1011 pack.plugin_name.value,
1012 to_span(auth_data),
1013 };
1014
1015 return ctx.check_extra_bytes();
1016 }
1017
1018 boost::mysql::detail::handhake_server_response boost::mysql::detail::deserialize_handshake_server_response(
1019 span<const std::uint8_t> buff,
1020 db_flavor flavor,
1021 diagnostics& diag
1022 )
1023 {
1024 deserialization_context ctx(buff);
1025 std::uint8_t msg_type = 0;
1026 auto err = to_error_code(deserialize(ctx, msg_type));
1027 if (err)
1028 return err;
1029
1030 if (msg_type == ok_packet_header)
1031 {
1032 ok_view ok{};
1033 err = deserialize_ok_packet(ctx.to_span(), ok);
1034 if (err)
1035 return err;
1036 return ok;
1037 }
1038 else if (msg_type == error_packet_header)
1039 {
1040 return process_error_packet(ctx.to_span(), flavor, diag);
1041 }
1042 else if (msg_type == auth_switch_request_header)
1043 {
1044
1045 auth_switch auth_sw{};
1046 auto err = deserialize_auth_switch(ctx.to_span(), auth_sw);
1047 if (err)
1048 return err;
1049 return auth_sw;
1050 }
1051 else if (msg_type == auth_more_data_header)
1052 {
1053
1054
1055 string_eof auth_more_data;
1056 auto err = deserialize(ctx, auth_more_data);
1057 BOOST_ASSERT(err == deserialize_errc::ok);
1058 boost::ignore_unused(err);
1059
1060
1061
1062
1063
1064 string_view challenge = auth_more_data.value;
1065 if (challenge == fast_auth_complete_challenge)
1066 {
1067 return handhake_server_response::ok_follows_t();
1068 }
1069
1070
1071 return handhake_server_response(to_span(challenge));
1072 }
1073 else
1074 {
1075
1076 return make_error_code(client_errc::protocol_value_error);
1077 }
1078 }
1079
1080 std::size_t boost::mysql::detail::auth_switch_response::get_size() const noexcept
1081 {
1082 return ::boost::mysql::detail::get_size(string_eof{to_string(auth_plugin_data)});
1083 }
1084
1085 void boost::mysql::detail::auth_switch_response::serialize(span<std::uint8_t> buff) const noexcept
1086 {
1087 BOOST_ASSERT(buff.size() >= get_size());
1088 serialization_context ctx(buff.data());
1089 ::boost::mysql::detail::serialize(ctx, string_eof{to_string(auth_plugin_data)});
1090 }
1091
1092 #endif