diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 389b79062aa..a649d6f1f5b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -130,6 +130,7 @@ pub mod ffi; pub mod feature; pub mod sdp; pub mod ldap; +pub mod mysql; #[allow(unused_imports)] pub use suricata_lua_sys; diff --git a/rust/src/mysql/mod.rs b/rust/src/mysql/mod.rs new file mode 100644 index 00000000000..702e20db911 --- /dev/null +++ b/rust/src/mysql/mod.rs @@ -0,0 +1,22 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +//! MySQL parser, logger and application layer module. +//! +//! written by Kotodian +pub mod mysql; +pub mod parser; diff --git a/rust/src/mysql/mysql.rs b/rust/src/mysql/mysql.rs new file mode 100644 index 00000000000..112163f58d9 --- /dev/null +++ b/rust/src/mysql/mysql.rs @@ -0,0 +1,1133 @@ +use std::collections::VecDeque; +use std::ffi::CString; + +use nom7::IResult; + +use crate::applayer::*; +use crate::conf::{conf_get, get_memval}; +use crate::core::*; + +use super::parser::*; + +pub const MYSQL_CONFIG_DEFAULT_STREAM_DEPTH: u32 = 0; + +static mut MYSQL_MAX_TX: usize = 1024; + +static mut ALPROTO_MYSQL: AppProto = ALPROTO_UNKNOWN; + +#[derive(FromPrimitive, Debug, AppLayerEvent)] +pub enum MysqlEvent { + TooManyTransactions, +} + +#[derive(Debug)] +pub struct MysqlTransaction { + pub tx_id: u64, + + pub version: Option, + pub command_code: Option, + pub command: Option, + pub affected_rows: Option, + pub rows: Option>, + pub tls: Option, + + pub complete: bool, + pub tx_data: AppLayerTxData, +} + +impl Transaction for MysqlTransaction { + fn id(&self) -> u64 { + self.tx_id + } +} + +impl Default for MysqlTransaction { + fn default() -> Self { + Self::new() + } +} + +impl MysqlTransaction { + pub fn new() -> Self { + Self { + tx_id: 0, + version: None, + command_code: None, + command: None, + affected_rows: None, + tls: None, + tx_data: AppLayerTxData::new(), + complete: false, + rows: None, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MysqlStateProgress { + // Default State + Init, + + // Connection Phase + // Server send HandshakeRequest to Client + Handshake, + // Client send HandshakeResponse to Server + // Server send AuthSwitchRequest to Client + Auth, + // Server send OkResponse to Client + AuthFinished, + + // Command Phase + // Client send QueryRequest to Server + CommandReceived, + // Server send EOF with 0x0A to Client + TextResulsetContinue, + // Server send QueryResponse to Client or Ok to Client + CommandResponseReceived, + // Server send LocalFileRequest with zero length to Client + LocalFileRequestReceived, + // Client send empty packet to Server + LocalFileContentFinished, + // Client send StatisticsRequest to Server + StatisticsReceived, + // Server send StatisticsResponse to client + StatisticsResponseReceived, + // Client send FieldList to Server + FieldListReceived, + // Server send FieldListResponse to client + FieldListResponseReceived, + // Client send ChangeUserRequest to Server + ChangeUserReceived, + // Server send OkResponse to client + ChangeUserResponseReceived, + // Client send Unknown to Server + UnknownCommandReceived, + // Client send StmtPrepareRequest to Server + StmtPrepareReceived, + // Server send StmtPrepareResponse to Client + StmtPrepareResponseReceived, + // Client send StmtExecRequest to Server + StmtExecReceived, + // Server send StmtExecResponse with EOF status equal 0x0a to Client + StmtExecResponseContinue, + // Server send ResultSetResponse to Client or Ok Response to Client + StmtExecResponseReceived, + // Client send StmtFetchRequest to Server + StmtFetchReceived, + // Server send StmtFetchResponse with EOF status equal 0x0a to Client + StmtFetchResponseContinue, + // Server send StmtFetchResponse to Client + StmtFetchResponseReceived, + // Client send StmtFetchRequest to Server + StmtResetReceived, + // Server send Ok or Err Response to Client + StmtResetResponseReceived, + // Client send StmtCloseRequest to Server + StmtCloseReceived, + + // Client send QueryRequest and command equal Quit to Server + // Client send ChangeUserRequest to server and server send ErrResponse to Client + // Transport Layer EOF + // Transport Layer Upgrade to TLS + Finished, +} + +#[derive(Debug)] +pub struct MysqlState { + pub state_data: AppLayerStateData, + pub tx_id: u64, + transactions: VecDeque, + request_gap: bool, + response_gap: bool, + state_progress: MysqlStateProgress, + tx_index_completed: usize, + + client_flags: u32, + version: Option, + tls: bool, + command_code: Option, + command: Option, + affected_rows: Option, + rows: Option>, + /// stmt prepare + prepare_stmt: Option, + /// stmt prepare response statement id + statement_id: Option, + /// stmt prepare param count + param_cnt: Option, + /// stmt prepare params definition + param_types: Option>, + /// stmt execute long data + stmt_long_datas: Vec, +} + +impl State for MysqlState { + fn get_transaction_count(&self) -> usize { + self.transactions.len() + } + + fn get_transaction_by_index(&self, index: usize) -> Option<&MysqlTransaction> { + self.transactions.get(index) + } +} + +impl Default for MysqlState { + fn default() -> Self { + Self::new() + } +} + +impl MysqlState { + pub fn new() -> Self { + let state = Self { + state_data: AppLayerStateData::new(), + tx_id: 0, + transactions: VecDeque::new(), + request_gap: false, + response_gap: false, + state_progress: MysqlStateProgress::Init, + tx_index_completed: 0, + + client_flags: 0, + version: None, + tls: false, + command_code: None, + command: None, + affected_rows: None, + rows: None, + prepare_stmt: None, + statement_id: None, + param_cnt: None, + param_types: None, + stmt_long_datas: Vec::new(), + }; + state + } + + pub fn free_tx(&mut self, tx_id: u64) { + let len = self.transactions.len(); + let mut found = false; + let mut index = 0; + for i in 0..len { + let tx = &self.transactions[i]; + if tx.tx_id == tx_id + 1 { + found = true; + index = i; + break; + } + } + if found { + self.tx_index_completed = 0; + self.transactions.remove(index); + } + } + + pub fn get_tx(&mut self, tx_id: u64) -> Option<&MysqlTransaction> { + self.transactions.iter().find(|tx| tx.tx_id == tx_id + 1) + } + + fn set_event(tx: &mut MysqlTransaction, event: MysqlEvent) { + tx.tx_data.set_event(event as u8); + } + + fn new_tx( + &mut self, command_code: Option, command: Option, version: Option, + affected_rows: Option, rows: Option>, tls: bool, + ) -> MysqlTransaction { + let mut tx = MysqlTransaction::new(); + self.tx_id += 1; + tx.tx_id = self.tx_id; + tx.version = version; + tx.tls = Some(tls); + tx.command_code = command_code; + tx.command = command; + tx.affected_rows = affected_rows; + tx.rows = rows; + SCLogDebug!("Creating new transaction.tx_id: {}", tx.tx_id); + if self.transactions.len() > unsafe { MYSQL_MAX_TX } + self.tx_index_completed { + let mut index = self.tx_index_completed; + for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { + index += 1; + if !tx_old.complete { + tx_old.complete = true; + MysqlState::set_event(tx_old, MysqlEvent::TooManyTransactions); + break; + } + } + self.tx_index_completed = index; + } + tx + } + /// Find or create a new transaction + /// + /// If a new transaction is created, push that into state.transactions before returning &mut to last tx + /// If we can't find a transaction and we should not create one, we return None + /// The moment when this is called will may impact the logic of transaction tracking (e.g. when a tx is considered completed) + fn find_or_create_tx(&mut self) -> Option<&mut MysqlTransaction> { + match self.state_progress { + MysqlStateProgress::CommandResponseReceived + | MysqlStateProgress::Finished + | MysqlStateProgress::StmtExecResponseReceived => { + let command = self.command.take().map(|cmd| match cmd { + MysqlCommand::Quit => "quit".to_string(), + MysqlCommand::Query { query } => query, + MysqlCommand::Ping => "ping".to_string(), + _ => String::new(), + }); + let rows = self.rows.take(); + let version = self.version.clone(); + let command_code = self.command_code.take(); + let affected_rows = self.affected_rows.take(); + let tls = self.tls; + + let tx = self.new_tx(command_code, command, version, affected_rows, rows, tls); + self.transactions.push_back(tx); + } + MysqlStateProgress::StmtCloseReceived => { + let _ = self.prepare_stmt.take(); + let _ = self.statement_id.take(); + let _ = self.param_types.take(); + } + _ => {} + } + // If we don't need a new transaction, just return the current one + SCLogDebug!("find_or_create state is {:?}", &self.state_progress); + self.transactions.back_mut() + } + + fn is_tx_completed(&self) -> bool { + if let MysqlStateProgress::CommandResponseReceived + | MysqlStateProgress::StmtExecResponseReceived + | MysqlStateProgress::Finished = self.state_progress + { + true + } else { + false + } + } + + fn request_next_state( + &mut self, request: MysqlFEMessage, f: *const Flow, + ) -> Option { + match request { + MysqlFEMessage::HandshakeResponse(resp) => { + // for now, don't support compress + if resp.zstd_compression_level.is_some() { + return Some(MysqlStateProgress::Finished); + } + if resp.client_flags & CLIENT_DEPRECATE_EOF != 0 + || resp.client_flags & CLIENT_OPTIONAL_RESULTSET_METADATA != 0 + { + return Some(MysqlStateProgress::Finished); + } + self.client_flags = resp.client_flags; + Some(MysqlStateProgress::Auth) + } + MysqlFEMessage::SSLRequest(_) => { + unsafe { + AppLayerRequestProtocolTLSUpgrade(f); + } + self.tls = true; + Some(MysqlStateProgress::Finished) + } + MysqlFEMessage::AuthRequest => None, + MysqlFEMessage::LocalFileData(length) => { + if length == 0 { + return Some(MysqlStateProgress::LocalFileContentFinished); + } + None + } + MysqlFEMessage::Request(req) => { + self.command_code = Some(req.command_code); + match req.command { + MysqlCommand::Query { query: _ } => { + self.command = Some(req.command); + return Some(MysqlStateProgress::CommandReceived); + } + MysqlCommand::StmtPrepare { query } => { + self.prepare_stmt = Some(query); + return Some(MysqlStateProgress::StmtPrepareReceived); + } + + MysqlCommand::StmtExecute { + statement_id, + params, + } => { + if let Some(expected_statement_id) = self.statement_id { + if statement_id == expected_statement_id { + if let Some(params) = params { + if !params.is_empty() + && self.param_cnt.is_some() + && self.param_cnt.unwrap() as usize == params.len() + { + let mut params = params.iter(); + if let Some(prepare_stmt) = &self.prepare_stmt { + // replace `?` to params + let mut query = String::new(); + for part in prepare_stmt.split('?') { + query.push_str(part); + if let Some(param) = params.next() { + query.push_str(param); + } + } + self.command = Some(MysqlCommand::Query { query }); + } else { + SCLogWarning!("Receive stmt exec without stmt prepare or without stmt prepare response"); + return Some(MysqlStateProgress::Finished); + } + } + } + } else { + SCLogWarning!( + "Receive stmt exec statement_id {} not equal we need {}", + expected_statement_id, + statement_id + ); + return Some(MysqlStateProgress::Finished); + } + } else { + self.param_cnt = None; + SCLogWarning!( + "Receive stmt exec without stmt prepare response, should end" + ); + return Some(MysqlStateProgress::Finished); + } + return Some(MysqlStateProgress::StmtExecReceived); + } + MysqlCommand::StmtFetch { + statement_id: _, + number_rows: _, + } => { + return Some(MysqlStateProgress::StmtFetchReceived); + } + MysqlCommand::StmtSendLongData(stmt_long_data) => { + if let Some(statement_id) = self.statement_id { + if statement_id == stmt_long_data.statement_id { + self.stmt_long_datas.push(stmt_long_data); + } + } + None + } + MysqlCommand::StmtReset { statement_id } => { + if let Some(expected_statement_id) = self.statement_id { + if statement_id == expected_statement_id { + self.stmt_long_datas.clear(); + } + } + return Some(MysqlStateProgress::StmtResetReceived); + } + MysqlCommand::StmtClose { statement_id } => { + if let Some(expected_statement_id) = self.statement_id { + if statement_id == expected_statement_id { + self.statement_id = None; + self.prepare_stmt = None; + self.stmt_long_datas.clear(); + self.param_types = None; + } else { + SCLogWarning!( + "Receive stmt close statement_id {} not equal we need {}", + expected_statement_id, + statement_id + ); + } + } else { + SCLogWarning!("Receive stmt close without stmt prepare response"); + } + + return Some(MysqlStateProgress::StmtCloseReceived); + } + MysqlCommand::Quit => { + self.command = Some(req.command); + return Some(MysqlStateProgress::Finished); + } + MysqlCommand::Ping + | MysqlCommand::Debug + | MysqlCommand::ResetConnection + | MysqlCommand::SetOption => { + self.command = Some(req.command); + Some(MysqlStateProgress::CommandReceived) + } + MysqlCommand::Statistics => Some(MysqlStateProgress::StatisticsReceived), + MysqlCommand::FieldList { table: _ } => { + self.command = Some(req.command); + return Some(MysqlStateProgress::FieldListReceived); + } + MysqlCommand::ChangeUser => { + self.command = Some(req.command); + return Some(MysqlStateProgress::ChangeUserReceived); + } + _ => { + SCLogWarning!("Unknown command {}", req.command_code); + return Some(MysqlStateProgress::UnknownCommandReceived); + } + } + } + } + } + + fn state_based_req_parsing( + state: MysqlStateProgress, i: &[u8], param_cnt: Option, + param_types: Option>, + stmt_long_datas: Option>, client_flags: u32, + ) -> IResult<&[u8], MysqlFEMessage> { + match state { + MysqlStateProgress::Handshake => { + let (i, client_flags) = parse_handshake_capabilities(i)?; + if client_flags & CLIENT_SSL != 0 { + let (i, req) = parse_handshake_ssl_request(i)?; + return Ok((i, MysqlFEMessage::SSLRequest(req))); + } + let (i, req) = parse_handshake_response(i, client_flags)?; + Ok((i, MysqlFEMessage::HandshakeResponse(req))) + } + MysqlStateProgress::Auth => { + let (i, _) = parse_auth_request(i)?; + Ok((i, MysqlFEMessage::AuthRequest)) + } + MysqlStateProgress::LocalFileRequestReceived => { + let (i, length) = parse_local_file_data_content(i)?; + Ok((i, MysqlFEMessage::LocalFileData(length))) + } + _ => { + let (i, req) = + parse_request(i, param_cnt, param_types, stmt_long_datas, client_flags)?; + Ok((i, MysqlFEMessage::Request(req))) + } + } + } + + pub fn parse_request(&mut self, flow: *const Flow, i: &[u8]) -> AppLayerResult { + // We're not interested in empty requests. + if i.is_empty() { + return AppLayerResult::ok(); + } + + // SCLogDebug!("input length: {}", i.len()); + + // If there was gap, check we can sync up again. + if self.request_gap { + if !probe(i) { + SCLogDebug!("Suricata interprets there's a gap in the request"); + return AppLayerResult::ok(); + } + + // It looks like we're in sync with the message header + // clear gap state and keep parsing. + self.request_gap = false; + } + if self.state_progress == MysqlStateProgress::Finished { + return AppLayerResult::ok(); + } + + let mut start = i; + while !start.is_empty() { + SCLogDebug!( + "In 'parse_request' State Progress is: {:?}", + &self.state_progress + ); + let mut stmt_long_datas = None; + if !self.stmt_long_datas.is_empty() { + stmt_long_datas = Some(self.stmt_long_datas.clone()); + } + + match MysqlState::state_based_req_parsing( + self.state_progress, + start, + self.param_cnt, + self.param_types.clone(), + stmt_long_datas, + self.client_flags, + ) { + Ok((rem, request)) => { + SCLogDebug!("Request is {:?}", &request); + // SCLogDebug!("remain length: {}", rem.len()); + start = rem; + if let Some(state) = self.request_next_state(request, flow) { + self.state_progress = state; + } + let tx_completed = self.is_tx_completed(); + if let Some(tx) = self.find_or_create_tx() { + if tx_completed { + tx.complete = true; + } + } + } + Err(nom7::Err::Incomplete(_needed)) => { + let consumed = i.len() - start.len(); + let needed_estimation = start.len() + 1; + SCLogDebug!( + "Needed: {:?}, estimated needed: {:?}", + _needed, + needed_estimation + ); + return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32); + } + Err(err) => { + SCLogError!( + "Error while parsing MySQL request, state: {:?} err: {:?}", + self.state_progress, + err + ); + return AppLayerResult::err(); + } + } + } + + // All Input was fully consumed. + AppLayerResult::ok() + } + + /// When the state changes based on a specific response, there are other actions we may need to perform + /// + /// If there is data from the backend message that Suri should store separately in the State or + /// Transaction, that is also done here + fn response_next_state( + &mut self, response: MysqlBEMessage, _f: *const Flow, + ) -> Option { + match response { + MysqlBEMessage::HandshakeRequest(req) => { + self.version = Some(req.version.clone()); + Some(MysqlStateProgress::Handshake) + } + + MysqlBEMessage::Response(resp) => match resp.item { + MysqlResponsePacket::LocalInFileRequest => { + Some(MysqlStateProgress::LocalFileRequestReceived) + } + MysqlResponsePacket::FieldsList { columns: _ } => { + Some(MysqlStateProgress::FieldListResponseReceived) + } + MysqlResponsePacket::Statistics => { + Some(MysqlStateProgress::StatisticsResponseReceived) + } + MysqlResponsePacket::AuthSwithRequest => Some(MysqlStateProgress::Auth), + MysqlResponsePacket::AuthData => None, + MysqlResponsePacket::Err { .. } => match self.state_progress { + MysqlStateProgress::CommandReceived + | MysqlStateProgress::TextResulsetContinue => { + Some(MysqlStateProgress::CommandResponseReceived) + } + MysqlStateProgress::FieldListReceived => { + Some(MysqlStateProgress::FieldListResponseReceived) + } + MysqlStateProgress::StmtExecReceived + | MysqlStateProgress::StmtExecResponseContinue => { + Some(MysqlStateProgress::StmtExecResponseReceived) + } + MysqlStateProgress::StmtResetReceived => { + Some(MysqlStateProgress::StmtResetResponseReceived) + } + MysqlStateProgress::ChangeUserReceived => { + Some(MysqlStateProgress::Finished) + } + MysqlStateProgress::StmtFetchReceived + | MysqlStateProgress::StmtFetchResponseContinue => { + Some(MysqlStateProgress::StmtFetchResponseReceived) + } + _ => None, + }, + MysqlResponsePacket::Ok { + rows, + flags: _, + warnings: _, + } => match self.state_progress { + MysqlStateProgress::Auth => Some(MysqlStateProgress::AuthFinished), + MysqlStateProgress::CommandReceived => { + self.affected_rows = Some(rows); + Some(MysqlStateProgress::CommandResponseReceived) + } + MysqlStateProgress::StmtExecReceived => { + self.affected_rows = Some(rows); + Some(MysqlStateProgress::StmtExecResponseReceived) + } + MysqlStateProgress::ChangeUserReceived => { + Some(MysqlStateProgress::ChangeUserResponseReceived) + } + MysqlStateProgress::StmtResetReceived => { + Some(MysqlStateProgress::StmtResetResponseReceived) + } + MysqlStateProgress::TextResulsetContinue => { + Some(MysqlStateProgress::CommandResponseReceived) + } + MysqlStateProgress::StmtExecResponseContinue => { + Some(MysqlStateProgress::StmtExecResponseReceived) + } + MysqlStateProgress::StmtFetchResponseContinue => { + Some(MysqlStateProgress::StmtFetchResponseReceived) + } + _ => None, + }, + MysqlResponsePacket::ResultSet { + n_cols: _, + columns: _, + eof, + rows, + } => { + let mut rows = rows.into_iter().map(|row| row.texts.join(",")).collect(); + if eof.status_flags != 0x0A { + self.rows = Some(rows); + Some(MysqlStateProgress::CommandResponseReceived) + } else { + // MultiStatement + if let Some(state_rows) = self.rows.as_mut() { + state_rows.append(&mut rows); + } else { + self.rows = Some(rows); + } + + Some(MysqlStateProgress::TextResulsetContinue) + } + } + MysqlResponsePacket::StmtPrepare { + statement_id, + num_params, + params, + .. + } => { + self.statement_id = Some(statement_id); + self.param_cnt = Some(num_params); + self.param_types = params; + Some(MysqlStateProgress::StmtPrepareResponseReceived) + } + MysqlResponsePacket::StmtFetch => { + Some(MysqlStateProgress::StmtFetchResponseReceived) + } + MysqlResponsePacket::BinaryResultSet { + n_cols: _, + eof, + rows, + } => { + if self.state_progress == MysqlStateProgress::StmtFetchReceived + || self.state_progress == MysqlStateProgress::StmtFetchResponseContinue + { + return Some(MysqlStateProgress::StmtFetchResponseContinue); + } + let mut rows = rows + .into_iter() + .map(|row| match row { + MysqlResultBinarySetRow::Err => String::new(), + MysqlResultBinarySetRow::Text(text) => text, + }) + .collect::>(); + if eof.status_flags != 0x0A { + self.rows = Some(rows); + Some(MysqlStateProgress::StmtExecResponseReceived) + } else { + // MultiResulset + if let Some(state_rows) = self.rows.as_mut() { + state_rows.append(&mut rows); + } else { + self.rows = Some(rows); + } + + Some(MysqlStateProgress::StmtExecResponseContinue) + } + } + _ => None, + }, + } + } + + fn state_based_resp_parsing( + state: MysqlStateProgress, i: &[u8], client_flags: u32, + ) -> IResult<&[u8], MysqlBEMessage> { + match state { + MysqlStateProgress::Init => { + let (i, resp) = parse_handshake_request(i)?; + Ok((i, MysqlBEMessage::HandshakeRequest(resp))) + } + + MysqlStateProgress::Auth => { + let (i, resp) = parse_auth_responsev2(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + + MysqlStateProgress::StmtPrepareReceived => { + let (i, resp) = parse_stmt_prepare_response(i, client_flags)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + + MysqlStateProgress::StmtExecReceived + | MysqlStateProgress::StmtExecResponseContinue => { + let (i, resp) = parse_stmt_execute_response(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + + MysqlStateProgress::StmtFetchReceived + | MysqlStateProgress::StmtFetchResponseContinue => { + let (i, resp) = parse_stmt_fetch_response(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + + MysqlStateProgress::FieldListReceived => { + let (i, resp) = parse_field_list_response(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + MysqlStateProgress::StatisticsReceived => { + let (i, resp) = parse_statistics_response(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + MysqlStateProgress::ChangeUserReceived => { + let (i, resp) = parse_change_user_response(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + + _ => { + let (i, resp) = parse_response(i)?; + Ok((i, MysqlBEMessage::Response(resp))) + } + } + } + + fn invalid_state_resp(&self) -> bool { + use MysqlStateProgress::*; + self.state_progress == CommandResponseReceived + || self.state_progress == StmtCloseReceived + || self.state_progress == StmtPrepareResponseReceived + || self.state_progress == StmtExecResponseReceived + } + + pub fn parse_response(&mut self, flow: *const Flow, i: &[u8]) -> AppLayerResult { + // We're not interested in empty responses. + if i.is_empty() { + return AppLayerResult::ok(); + } + + if self.response_gap { + if !probe(i) { + SCLogDebug!("Suricata interprets there's a gap in the response"); + return AppLayerResult::ok(); + } + + // It seems we're in sync with a message header, clear gap state and keep parsing. + self.response_gap = false; + } + + let mut start = i; + + while !start.is_empty() { + if self.state_progress == MysqlStateProgress::Finished || self.invalid_state_resp() { + return AppLayerResult::ok(); + } + match MysqlState::state_based_resp_parsing( + self.state_progress, + start, + self.client_flags, + ) { + Ok((rem, response)) => { + start = rem; + + SCLogDebug!("Response is {:?}", &response); + if let Some(state) = self.response_next_state(response, flow) { + self.state_progress = state; + } + let tx_completed = self.is_tx_completed(); + if let Some(tx) = self.find_or_create_tx() { + if tx_completed { + tx.complete = true; + } + } + } + Err(nom7::Err::Incomplete(_needed)) => { + let consumed = i.len() - start.len(); + let needed_estimation = start.len() + 1; + SCLogDebug!( + "Needed: {:?}, estimated needed: {:?}, start is {:?}", + _needed, + needed_estimation, + &start + ); + return AppLayerResult::incomplete(consumed as u32, needed_estimation as u32); + } + Err(err) => { + SCLogError!( + "Error while parsing MySQL response, state: {:?} err: {:?}", + self.state_progress, + err, + ); + return AppLayerResult::err(); + } + } + } + + // All Input was fully consumed. + AppLayerResult::ok() + } + + pub fn on_request_gap(&mut self, _size: u32) { + self.request_gap = true; + } + + pub fn on_response_gap(&mut self, _size: u32) { + self.response_gap = true; + } +} + +/// Probe for a valid mysql message +pub fn probe(input: &[u8]) -> bool { + if parse_packet_header(input).is_ok() { + return true; + } + SCLogDebug!("probe is false"); + false +} + +// C exports + +/// C entry point for a probing parser. +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_probing_ts( + _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, +) -> AppProto { + if input_len >= 1 && !input.is_null() { + let slice: &[u8] = build_slice!(input, input_len as usize); + match parse_packet_header(slice) { + Ok(_) => return ALPROTO_MYSQL, + Err(nom7::Err::Incomplete(_)) => return ALPROTO_UNKNOWN, + Err(err) => { + SCLogError!("failed to probe request {:?}", err); + return ALPROTO_FAILED; + } + } + } + + return ALPROTO_UNKNOWN; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_probing_tc( + _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, +) -> AppProto { + if input_len >= 1 && !input.is_null() { + let slice: &[u8] = build_slice!(input, input_len as usize); + match parse_handshake_request(slice) { + Ok(_) => return ALPROTO_MYSQL, + Err(nom7::Err::Incomplete(_)) => return ALPROTO_UNKNOWN, + Err(err) => { + SCLogError!("failed to probe response {:?}", err); + return ALPROTO_FAILED; + } + } + } + + return ALPROTO_UNKNOWN; +} + +#[no_mangle] +pub extern "C" fn rs_mysql_state_new( + _orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto, +) -> *mut std::os::raw::c_void { + let state = MysqlState::new(); + let boxed = Box::new(state); + return Box::into_raw(boxed) as *mut _; +} + +#[no_mangle] +pub extern "C" fn rs_mysql_state_free(state: *mut std::os::raw::c_void) { + std::mem::drop(unsafe { Box::from_raw(state as *mut MysqlState) }); +} + +#[no_mangle] +pub extern "C" fn rs_mysql_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { + let state_safe: &mut MysqlState; + unsafe { + state_safe = cast_pointer!(state, MysqlState); + } + state_safe.free_tx(tx_id); +} + +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_parse_request( + flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, _data: *const std::os::raw::c_void, +) -> AppLayerResult { + if stream_slice.is_empty() { + if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 { + SCLogDebug!(" Caracal reached `eof`"); + return AppLayerResult::ok(); + } else { + return AppLayerResult::err(); + } + } + + let state_safe: &mut MysqlState = cast_pointer!(state, MysqlState); + + if stream_slice.is_gap() { + state_safe.on_request_gap(stream_slice.gap_size()); + } else if !stream_slice.is_empty() { + return state_safe.parse_request(flow, stream_slice.as_slice()); + } + AppLayerResult::ok() +} + +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_parse_response( + flow: *const Flow, state: *mut std::os::raw::c_void, pstate: *mut std::os::raw::c_void, + stream_slice: StreamSlice, _data: *const std::os::raw::c_void, +) -> AppLayerResult { + if stream_slice.is_empty() { + if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 { + return AppLayerResult::ok(); + } else { + return AppLayerResult::err(); + } + } + + let state_safe: &mut MysqlState = cast_pointer!(state, MysqlState); + + if stream_slice.is_gap() { + state_safe.on_response_gap(stream_slice.gap_size()); + } else if !stream_slice.is_empty() { + return state_safe.parse_response(flow, stream_slice.as_slice()); + } + AppLayerResult::ok() +} + +#[no_mangle] +pub extern "C" fn rs_mysql_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { + let state_safe: &mut MysqlState; + unsafe { + state_safe = cast_pointer!(state, MysqlState); + } + return state_safe.tx_id; +} + +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_state_get_tx( + state: *mut std::os::raw::c_void, tx_id: u64, +) -> *mut std::os::raw::c_void { + let state_safe: &mut MysqlState = cast_pointer!(state, MysqlState); + match state_safe.get_tx(tx_id) { + Some(tx) => { + return tx as *const _ as *mut _; + } + None => { + return std::ptr::null_mut(); + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_tx_get_alstate_progress( + tx: *mut std::os::raw::c_void, _direction: u8, +) -> std::os::raw::c_int { + let tx = cast_pointer!(tx, MysqlTransaction); + if tx.complete { + return 1; + } + return 0; +} + +export_tx_data_get!(rs_mysql_get_tx_data, MysqlTransaction); +export_state_data_get!(rs_mysql_get_state_data, MysqlState); + +/// Get the mysql query +#[no_mangle] +pub unsafe extern "C" fn SCMysqlTxGetCommandName( + tx: &mut MysqlTransaction, buf: *mut *const u8, len: *mut u32, +) -> bool { + if let Some(command) = &tx.command { + if !command.is_empty() { + *buf = command.as_ptr(); + *len = command.len() as u32; + return true; + } + } + + false +} + +/// Get the mysql rows at index i +#[no_mangle] +pub unsafe extern "C" fn SCMysqlGetRowsData( + tx: &mut MysqlTransaction, i: u32, buf: *mut *const u8, len: *mut u32, +) -> bool { + if let Some(rows) = &tx.rows { + if !rows.is_empty() { + let index = i as usize; + if let Some(row) = rows.get(index) { + if !row.is_empty() { + *buf = row.as_ptr(); + *len = row.len() as u32; + return true; + } + } + } + } + + false +} + +// Parser name as a C style string. +const PARSER_NAME: &[u8] = b"mysql\0"; + +#[no_mangle] +pub unsafe extern "C" fn rs_mysql_register_parser() { + let default_port = CString::new("[3306]").unwrap(); + let mut stream_depth = MYSQL_CONFIG_DEFAULT_STREAM_DEPTH; + let parser = RustParser { + name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, + default_port: default_port.as_ptr(), + ipproto: IPPROTO_TCP, + probe_ts: Some(rs_mysql_probing_ts), + probe_tc: Some(rs_mysql_probing_tc), + min_depth: 0, + max_depth: 16, + state_new: rs_mysql_state_new, + state_free: rs_mysql_state_free, + tx_free: rs_mysql_state_tx_free, + parse_ts: rs_mysql_parse_request, + parse_tc: rs_mysql_parse_response, + get_tx_count: rs_mysql_state_get_tx_count, + get_tx: rs_mysql_state_get_tx, + tx_comp_st_ts: 1, + tx_comp_st_tc: 1, + tx_get_progress: rs_mysql_tx_get_alstate_progress, + get_eventinfo: Some(MysqlEvent::get_event_info), + get_eventinfo_byid: Some(MysqlEvent::get_event_info_by_id), + localstorage_new: None, + localstorage_free: None, + get_tx_files: None, + get_tx_iterator: Some( + crate::applayer::state_get_tx_iterator::, + ), + get_tx_data: rs_mysql_get_tx_data, + get_state_data: rs_mysql_get_state_data, + apply_tx_config: None, + flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS, + get_frame_id_by_name: None, + get_frame_name_by_id: None, + }; + + let ip_proto_str = CString::new("tcp").unwrap(); + + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_MYSQL = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } + SCLogDebug!("Rust mysql parser registered."); + let retval = conf_get("app-layer.protocols.mysql.stream-depth"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { + stream_depth = retval as u32; + } + Err(_) => { + SCLogError!("Invalid depth value"); + } + } + AppLayerParserSetStreamDepth(IPPROTO_TCP, ALPROTO_MYSQL, stream_depth) + } + if let Some(val) = conf_get("app-layer.protocols.mysql.max-tx") { + if let Ok(v) = val.parse::() { + MYSQL_MAX_TX = v; + } else { + SCLogError!("Invalid value for mysql.max-tx"); + } + } + } else { + SCLogDebug!("Protocol detector and parser disabled for MYSQL."); + } +} diff --git a/rust/src/mysql/parser.rs b/rust/src/mysql/parser.rs new file mode 100644 index 00000000000..43e230028b1 --- /dev/null +++ b/rust/src/mysql/parser.rs @@ -0,0 +1,3350 @@ +/* Copyright (C) 2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +// Author: Kotodian + +//! MySQL nom parsers + +use nom7::{ + bytes::streaming::{take, take_till}, + combinator::{cond, map, verify}, + multi::{many_m_n, many_till}, + number::streaming::{ + be_i8, be_u32, be_u8, le_f32, le_f64, le_i16, le_i32, le_i64, le_u16, le_u24, le_u32, + le_u64, + }, + IResult, +}; +use num::ToPrimitive; + +#[allow(dead_code)] +pub const CLIENT_LONG_PASSWORD: u32 = BIT_U32!(0); +#[allow(dead_code)] +pub const CLIENT_FOUND_ROWS: u32 = BIT_U32!(1); +#[allow(dead_code)] +pub const CLIENT_LONG_FLAG: u32 = BIT_U32!(2); +#[allow(dead_code)] +const CLIENT_CONNECT_WITH_DB: u32 = BIT_U32!(3); +#[allow(dead_code)] +const CLIENT_NO_SCHEMA: u32 = BIT_U32!(4); +#[allow(dead_code)] +const CLIENT_COMPRESS: u32 = BIT_U32!(5); +#[allow(dead_code)] +const CLIENT_ODBC: u32 = BIT_U32!(6); +#[allow(dead_code)] +const CLIENT_LOCAL_FILES: u32 = BIT_U32!(7); +#[allow(dead_code)] +const CLIENT_IGNORE_SPACE: u32 = BIT_U32!(8); +#[allow(dead_code)] +const CLIENT_PROTOCOL_41: u32 = BIT_U32!(9); +#[allow(dead_code)] +const CLIENT_INTERACTIVE: u32 = BIT_U32!(10); +#[allow(dead_code)] +pub const CLIENT_SSL: u32 = BIT_U32!(11); +#[allow(dead_code)] +pub const CLIENT_IGNORE_SIGPIPE: u32 = BIT_U32!(12); +#[allow(dead_code)] +pub const CLIENT_TRANSACTIONS: u32 = BIT_U32!(13); +#[allow(dead_code)] +pub const CLIENT_RESERVED: u32 = BIT_U32!(14); +#[allow(dead_code)] +pub const CLIENT_RESERVED2: u32 = BIT_U32!(15); +#[allow(dead_code)] +pub const CLIENT_MULTI_STATEMENTS: u32 = BIT_U32!(16); +#[allow(dead_code)] +pub const CLIENT_MULTI_RESULTS: u32 = BIT_U32!(17); +#[allow(dead_code)] +pub const CLIENT_PS_MULTI_RESULTS: u32 = BIT_U32!(18); +#[allow(dead_code)] +pub const CLIENT_PLUGIN_AUTH: u32 = BIT_U32!(19); +#[allow(dead_code)] +pub const CLIENT_CONNECT_ATTRS: u32 = BIT_U32!(20); +#[allow(dead_code)] +pub const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA: u32 = BIT_U32!(21); +#[allow(dead_code)] +pub const CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS: u32 = BIT_U32!(22); +#[allow(dead_code)] +pub const CLIENT_SESSION_TRACK: u32 = BIT_U32!(23); +#[allow(dead_code)] +pub const CLIENT_DEPRECATE_EOF: u32 = BIT_U32!(24); +#[allow(dead_code)] +pub const CLIENT_OPTIONAL_RESULTSET_METADATA: u32 = BIT_U32!(25); +#[allow(dead_code)] +pub const CLIENT_ZSTD_COMPRESSION_ALGORITHM: u32 = BIT_U32!(26); +#[allow(dead_code)] +pub const CLIENT_QUERY_ATTRIBUTES: u32 = BIT_U32!(27); +#[allow(dead_code)] +pub const MULTI_FACTOR_AUTHENTICATION: u32 = BIT_U32!(28); +#[allow(dead_code)] +pub const CLIENT_CAPABILITY_EXTENSION: u32 = BIT_U32!(29); +#[allow(dead_code)] +pub const CLIENT_SSL_VERIFY_SERVER_CERT: u32 = BIT_U32!(30); +#[allow(dead_code)] +pub const CLIENT_REMEMBER_OPTIONS: u32 = BIT_U32!(31); + +#[allow(dead_code)] +pub const FIELD_FLAGS_UNSIGNED: u32 = BIT_U32!(5); + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum FieldType { + Decimal = 0, + Tiny = 1, + Short = 2, + Long = 3, + Float = 4, + Double = 5, + NULL = 6, + Timestamp = 7, + LongLong = 8, + Int24 = 9, + Date = 10, + Time = 11, + Datetime = 12, + Year = 13, + NewDate = 14, + Varchar = 15, + Bit = 16, + Timestamp2 = 17, + Datetime2 = 18, + Time2 = 19, + Array = 20, + Unknown = 241, + Vector = 242, + Invalid = 243, + Bool = 244, + Json = 245, + NewDecimal = 246, + Enum = 247, + Set = 248, + TinyBlob = 249, + MediumBlob = 250, + LongBlob = 251, + Blob = 252, + VarString = 253, + String = 254, + Geometry = 255, +} + +impl From for FieldType { + fn from(value: u8) -> Self { + match value { + 0 => Self::Decimal, + 1 => Self::Tiny, + 2 => Self::Short, + 3 => Self::Long, + 4 => Self::Float, + 5 => Self::Double, + 6 => Self::NULL, + 7 => Self::Timestamp, + 8 => Self::LongLong, + 9 => Self::Int24, + 10 => Self::Date, + 11 => Self::Time, + 12 => Self::Datetime, + 13 => Self::Year, + 15 => Self::Varchar, + 16 => Self::Bit, + 17 => Self::Timestamp2, + 18 => Self::Datetime2, + 19 => Self::Time2, + 20 => Self::Array, + 242 => Self::Vector, + 243 => Self::Invalid, + 244 => Self::Bool, + 245 => Self::Json, + 246 => Self::NewDecimal, + 247 => Self::Enum, + 248 => Self::Set, + 249 => Self::TinyBlob, + 250 => Self::MediumBlob, + 251 => Self::LongBlob, + 252 => Self::Blob, + 253 => Self::VarString, + 254 => Self::String, + 255 => Self::Geometry, + _ => Self::Unknown, + } + } +} + +#[derive(Debug)] +pub struct MysqlPacket { + pub pkt_len: u32, + pub pkt_num: u8, +} + +#[derive(Debug)] +pub struct MysqlEofPacket { + pub warnings: u16, + pub status_flags: u16, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct StmtLongData { + pub statement_id: u32, + pub param_id: u16, + pub payload: String, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum MysqlCommand { + Unknown, + Quit, + Ping, + Statistics, + Debug, + ChangeUser, + ResetConnection, + SetOption, + InitDb { + schema: String, + }, + Query { + query: String, + }, + FieldList { + table: String, + }, + StmtPrepare { + query: String, + }, + StmtSendLongData(StmtLongData), + StmtExecute { + statement_id: u32, + params: Option>, + }, + StmtFetch { + statement_id: u32, + number_rows: u32, + }, + StmtReset { + statement_id: u32, + }, + StmtClose { + statement_id: u32, + }, +} + +#[derive(Debug, Clone)] +pub struct MysqlColumnDefinition { + pub catalog: String, + pub schema: String, + pub table: String, + pub orig_table: String, + pub name: String, + pub character_set: u16, + pub column_length: u32, + pub field_type: FieldType, + pub flags: u16, + pub decimals: u8, +} + +#[derive(Debug)] +pub struct MysqlResultSetRow { + pub texts: Vec, +} + +#[derive(Debug)] +pub enum MysqlResultBinarySetRow { + Err, + Text(String), +} + +#[derive(Debug)] +pub struct MysqlHandshakeRequest { + // pub header: MysqlPacket, + pub protocol: u8, + pub version: String, + pub conn_id: u32, + pub salt1: String, + pub capability_flags1: u16, + pub character_set: u8, + pub status_flags: u16, + pub capability_flags2: u16, + pub auth_plugin_len: u8, + pub salt2: String, + pub auth_plugin_data: Option, +} + +#[derive(Debug)] +pub struct MysqlHandshakeResponseAttribute { + pub key: String, + pub value: String, +} + +#[derive(Debug)] +pub struct MysqlSSLRequest { + pub filter: Option, +} + +#[derive(Debug)] +pub struct MysqlHandshakeResponse { + pub username: String, + pub auth_response_len: u8, + pub auth_response: String, + pub database: Option, + pub client_flags: u32, + pub client_plugin_name: Option, + pub attributes: Option>, + pub zstd_compression_level: Option, +} + +pub const SHA2_PASSWORD_REQUEST_PUBLIC_KEY: u8 = 2; +pub const SHA2_PASSWORD_FAST_AUTH_SUCCESS: u8 = 3; +pub const SHA2_PASSWORD_PERFORM_FULL_AUTH: u8 = 3; + +#[derive(Debug)] +pub struct MysqlAuthSwtichRequest { + pub plugin_name: String, + pub plugin_data: String, +} + +#[derive(Debug)] +pub struct MysqlRequest { + // pub header: MysqlPacket, + pub command_code: u8, + pub command: MysqlCommand, +} + +#[derive(Debug)] +pub enum MysqlResponsePacket { + Unknown, + AuthMoreData { + data: u8, + }, + LocalInFileRequest, + AuthData, + Statistics, + AuthSwithRequest, + EOF, + Ok { + rows: u64, + flags: u16, + warnings: u16, + }, + Err { + error_code: u16, + error_message: String, + }, + FieldsList { + columns: Option>, + }, + ResultSet { + n_cols: u64, + columns: Vec, + eof: MysqlEofPacket, + rows: Vec, + }, + BinaryResultSet { + n_cols: u64, + eof: MysqlEofPacket, + rows: Vec, + }, + + StmtPrepare { + statement_id: u32, + num_params: u16, + params: Option>, + fields: Option>, + }, + StmtFetch, +} + +#[derive(Debug)] +pub struct MysqlResponse { + pub item: MysqlResponsePacket, +} + +#[derive(Debug)] +pub enum MysqlBEMessage { + HandshakeRequest(MysqlHandshakeRequest), + Response(MysqlResponse), +} + +#[derive(Debug)] +pub enum MysqlFEMessage { + SSLRequest(MysqlSSLRequest), + AuthRequest, + Request(MysqlRequest), + LocalFileData(u32), + HandshakeResponse(MysqlHandshakeResponse), +} + +fn parse_varint(i: &[u8]) -> IResult<&[u8], u64> { + let (i, length) = be_u8(i)?; + match length { + // 251: NULL + 0xfb => Ok((i, 0)), + // 252: value of following 2 + 0xfc => { + let (i, v0) = be_u8(i)?; + let (i, v1) = be_u8(i)?; + let v0 = v0 as u64; + let v1 = (v1 as u64) << 8; + Ok((i, v0 | v1)) + } + // 253: value of following 3 + 0xfd => { + let (i, v0) = be_u8(i)?; + let (i, v1) = be_u8(i)?; + let (i, v2) = be_u8(i)?; + let v0 = v0 as u64; + let v1 = (v1 as u64) << 8; + let v2 = (v2 as u64) << 16; + Ok((i, v0 | v1 | v2)) + } + // 254: value of following 8 + 0xfe => { + let (i, v0) = be_u8(i)?; + let (i, v1) = be_u8(i)?; + let (i, v2) = be_u8(i)?; + let (i, v3) = be_u8(i)?; + let (i, v4) = be_u8(i)?; + let (i, v5) = be_u8(i)?; + let (i, v6) = be_u8(i)?; + let (i, v7) = be_u8(i)?; + let v0 = v0 as u64; + let v1 = (v1 as u64) << 8; + let v2 = (v2 as u64) << 16; + let v3 = (v3 as u64) << 24; + let v4 = (v4 as u64) << 32; + let v5 = (v5 as u64) << 40; + let v6 = (v6 as u64) << 48; + let v7 = (v7 as u64) << 56; + Ok((i, v0 | v1 | v2 | v3 | v4 | v5 | v6 | v7)) + } + _ => Ok((i, length as u64)), + } +} + +pub fn parse_packet_header(i: &[u8]) -> IResult<&[u8], MysqlPacket> { + let (i, pkt_len) = le_u24(i)?; + let (i, pkt_num) = be_u8(i)?; + Ok((i, MysqlPacket { pkt_len, pkt_num })) +} + +fn parse_eof_packet(i: &[u8]) -> IResult<&[u8], MysqlEofPacket> { + let (i, _header) = parse_packet_header(i)?; + let (i, _tag) = verify(be_u8, |&x| x == 0xfe)(i)?; + let (i, warnings) = le_u16(i)?; + let (i, status_flags) = le_u16(i)?; + + Ok(( + i, + MysqlEofPacket { + warnings, + status_flags, + }, + )) +} + +fn parse_init_db_cmd(i: &[u8], length: u32) -> IResult<&[u8], MysqlCommand> { + let (i, schema) = map(take(length), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + Ok((i, MysqlCommand::InitDb { schema })) +} + +fn parse_query_cmd(i: &[u8], length: u32, client_flags: u32) -> IResult<&[u8], MysqlCommand> { + let old = i; + let (i, param_cnt) = cond(client_flags & CLIENT_QUERY_ATTRIBUTES != 0, parse_varint)(i)?; + let (i, _param_set_cnt) = cond( + client_flags & CLIENT_QUERY_ATTRIBUTES != 0, + verify(be_u8, |¶m_set_cnt| param_set_cnt == 1), + )(i)?; + let param_cnt = param_cnt.unwrap_or_default(); + let (i, null_mask) = cond(param_cnt > 0, take((param_cnt + 7) / 8))(i)?; + let (i, new_params_bind_flag) = cond( + param_cnt > 0, + verify(be_u8, |&new_params_bind_flag| new_params_bind_flag == 1), + )(i)?; + let new_params_bind_flag = new_params_bind_flag.unwrap_or_default(); + + let (i, param_types) = cond( + param_cnt > 0 && new_params_bind_flag != 0, + many_m_n( + param_cnt as usize, + param_cnt as usize, + |i| -> IResult<&[u8], (FieldType, bool)> { + let (i, field_type) = be_u8(i)?; + let (i, flags) = be_u8(i)?; + let (i, _param_name) = map(take(length), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + Ok((i, (field_type.into(), flags != 0))) + }, + ), + )(i)?; + + let mut data = i; + if param_cnt > 0 { + let null_mask = null_mask.unwrap_or_default(); + if let Some(param_types) = param_types { + for i in 0..param_cnt as usize { + if !null_mask.is_empty() && ((null_mask[i >> 3] >> (i & 7)) & 1) == 1 { + continue; + } + let (field_type, unsigned) = param_types.get(i).unwrap(); + + let ch = data; + // Normal + let (ch, _res) = match *field_type { + FieldType::NULL => (ch, "NULL".to_string()), + FieldType::Tiny | FieldType::Bool => { + if *unsigned { + let (ch, v) = be_u8(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = be_i8(ch)?; + (ch, v.to_string()) + } + } + FieldType::Short | FieldType::Year => { + if *unsigned { + let (ch, v) = le_u16(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = le_i16(ch)?; + (ch, v.to_string()) + } + } + FieldType::Int24 | FieldType::Long => { + if *unsigned { + let (ch, v) = le_u32(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = le_i32(ch)?; + (ch, v.to_string()) + } + } + FieldType::LongLong => { + if *unsigned { + let (ch, v) = le_u64(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = le_i64(ch)?; + (ch, v.to_string()) + } + } + FieldType::Float => { + let (ch, v) = le_f32(ch)?; + (ch, v.to_string()) + } + FieldType::Double => { + let (ch, v) = le_f64(ch)?; + (ch, v.to_string()) + } + FieldType::Decimal + | FieldType::NewDecimal + | FieldType::Varchar + | FieldType::Bit + | FieldType::Enum + | FieldType::Set + | FieldType::TinyBlob + | FieldType::MediumBlob + | FieldType::LongBlob + | FieldType::Blob + | FieldType::VarString + | FieldType::String + | FieldType::Geometry + | FieldType::Json + | FieldType::Vector => { + let (ch, len) = parse_varint(ch)?; + let (ch, data) = map(take(len), |ch: &[u8]| { + String::from_utf8_lossy(ch).to_string() + })(ch)?; + (ch, data) + } + FieldType::Date + | FieldType::NewDate + | FieldType::Datetime + | FieldType::Datetime2 + | FieldType::Timestamp + | FieldType::Timestamp2 + | FieldType::Time + | FieldType::Time2 => { + let (ch, len) = parse_varint(ch)?; + match len { + 0 => (ch, "datetime 0000-00-00 00:00:00.000000".to_string()), + 4 => { + let (ch, year) = le_u16(ch)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + (ch, format!("datetime {:04}-{:02}-{:02}", year, month, day)) + } + 7 => { + let (ch, year) = le_u16(ch)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + let (ch, hour) = be_u8(ch)?; + let (ch, minute) = be_u8(ch)?; + let (ch, second) = be_u8(ch)?; + ( + ch, + format!( + "datetime {:04}-{:02}-{:02} {:02}:{:02}:{:02}", + year, month, day, hour, minute, second + ), + ) + } + 11 => { + let (ch, year) = le_u16(ch)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + let (ch, hour) = be_u8(ch)?; + let (ch, minute) = be_u8(ch)?; + let (ch, second) = be_u8(ch)?; + let (ch, microsecond) = le_u32(ch)?; + ( + ch, + format!( + "datetime {:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}", + year, month, day, hour, minute, second, microsecond, + ), + ) + } + _ => { + let (ch, _) = take(len)(ch)?; + (ch, "".to_string()) + } + } + } + _ => (ch, "".to_string()), + }; + data = ch; + } + } + } + let i = data; + + let consumed = (old.len() - i.len()) as u32; + + // Should never happen + if consumed > length { + return Ok(( + &[], + MysqlCommand::Query { + query: "".to_string(), + }, + )); + } + let length = length - consumed; + + let (i, query) = map(take(length), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + Ok((i, MysqlCommand::Query { query })) +} + +fn parse_stmt_prepare_cmd(i: &[u8], length: u32) -> IResult<&[u8], MysqlCommand> { + let (i, query) = map(take(length), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + Ok((i, MysqlCommand::StmtPrepare { query })) +} + +fn parse_stmt_send_long_data_cmd(i: &[u8]) -> IResult<&[u8], MysqlCommand> { + let (i, statement_id) = le_u32(i)?; + let (i, param_id) = le_u16(i)?; + let (i, length) = parse_varint(i)?; + let (i, payload) = map(take(length), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + Ok(( + i, + MysqlCommand::StmtSendLongData(StmtLongData { + statement_id, + param_id, + payload, + }), + )) +} + +fn parse_stmt_execute_cmd( + i: &[u8], param_cnt: Option, param_types: Option>, + stmt_long_datas: Option>, length: u32, client_flags: u32, +) -> IResult<&[u8], MysqlCommand> { + let old = i; + let (i, statement_id) = le_u32(i)?; + let (i, flags) = be_u8(i)?; + let (i, _iteration_count) = le_u32(i)?; + + if let Some(param_cnt) = param_cnt { + let mut param_cnt = param_cnt; + if param_cnt > 0 || ((client_flags & CLIENT_QUERY_ATTRIBUTES != 0) && (flags & 8 != 0)) { + let (i, override_param_cnts) = + cond(client_flags & CLIENT_QUERY_ATTRIBUTES != 0, parse_varint)(i)?; + if let Some(override_param_cnts) = override_param_cnts { + param_cnt = override_param_cnts as u16; + } + if param_cnt > 0 { + // NULL-bitmap, [(column-count + 7) / 8 bytes] + let null_bitmap_size = (param_cnt + 7) / 8; + let (i, null_mask) = take(null_bitmap_size)(i)?; + let (i, new_params_bind_flags) = be_u8(i)?; + + let (i, new_param_types) = cond( + new_params_bind_flags != 0, + many_m_n( + param_cnt as usize, + param_cnt as usize, + |ch| -> IResult<&[u8], (FieldType, bool)> { + let (ch, field_type) = be_u8(ch)?; + let (ch, flags) = be_u8(ch)?; + let (ch, _param_names) = + cond(client_flags & CLIENT_QUERY_ATTRIBUTES != 0, |ch| { + let (ch, length) = parse_varint(ch)?; + let (ch, name) = map(take(length), |s| { + String::from_utf8_lossy(s).to_string() + })(ch)?; + Ok((ch, name)) + })(ch)?; + + Ok((ch, (field_type.into(), flags != 0))) + }, + ), + )(i)?; + let param_types = if let Some(new_param_types) = new_param_types { + Some(new_param_types) + } else { + match param_types { + Some(param_types) => Some( + param_types + .iter() + .map(|param_type| (param_type.field_type, param_type.flags != 0)) + .collect(), + ), + None => None, + } + }; + + let consumed = (old.len() - i.len()) as u32; + // Should never happen + if consumed > length { + return Ok(( + &[], + MysqlCommand::StmtExecute { + statement_id, + params: None, + }, + )); + } + let (i, data) = take(length - consumed as u32)(i)?; + if param_types.is_none() { + return Ok(( + i, + MysqlCommand::StmtExecute { + statement_id, + params: None, + }, + )); + } + + let param_types = param_types.unwrap(); + + let mut data = data; + let mut params = Vec::new(); + for i in 0..param_cnt as usize { + // Field is NULL + // (byte >> bit-pos) % 2 == 1 + if !null_mask.is_empty() && ((null_mask[i >> 3] >> (i & 7)) & 1) == 1 { + params.push("NULL".to_string()); + continue; + } + // Field is LongData + if let Some(stmt_long_datas) = &stmt_long_datas { + for stmt_long_data in stmt_long_datas { + if stmt_long_data.param_id as usize == i { + params.push(stmt_long_data.payload.clone()); + continue; + } + } + } + let (field_type, unsigned) = param_types.get(i).unwrap(); + + let ch = data; + // Normal + let (ch, res) = match *field_type { + FieldType::NULL => (ch, "NULL".to_string()), + FieldType::Tiny | FieldType::Bool => { + if *unsigned { + let (ch, v) = be_u8(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = be_i8(ch)?; + (ch, v.to_string()) + } + } + FieldType::Short | FieldType::Year => { + if *unsigned { + let (ch, v) = le_u16(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = le_i16(ch)?; + (ch, v.to_string()) + } + } + FieldType::Int24 | FieldType::Long => { + if *unsigned { + let (ch, v) = le_u32(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = le_i32(ch)?; + (ch, v.to_string()) + } + } + FieldType::LongLong => { + if *unsigned { + let (ch, v) = le_u64(ch)?; + (ch, v.to_string()) + } else { + let (ch, v) = le_i64(ch)?; + (ch, v.to_string()) + } + } + FieldType::Float => { + let (ch, v) = le_f32(ch)?; + (ch, v.to_string()) + } + FieldType::Double => { + let (ch, v) = le_f64(ch)?; + (ch, v.to_string()) + } + FieldType::Decimal + | FieldType::NewDecimal + | FieldType::Varchar + | FieldType::Bit + | FieldType::Enum + | FieldType::Set + | FieldType::TinyBlob + | FieldType::MediumBlob + | FieldType::LongBlob + | FieldType::Blob + | FieldType::VarString + | FieldType::String + | FieldType::Geometry + | FieldType::Json + | FieldType::Vector => { + let (ch, len) = parse_varint(ch)?; + let (ch, data) = map(take(len), |ch: &[u8]| { + String::from_utf8_lossy(ch).to_string() + })(ch)?; + (ch, data) + } + FieldType::Date + | FieldType::NewDate + | FieldType::Datetime + | FieldType::Datetime2 + | FieldType::Timestamp + | FieldType::Timestamp2 + | FieldType::Time + | FieldType::Time2 => { + let (ch, len) = parse_varint(ch)?; + match len { + 0 => (ch, "datetime 0000-00-00 00:00:00.000000".to_string()), + 4 => { + let (ch, year) = le_u16(ch)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + (ch, format!("datetime {:04}-{:02}-{:02}", year, month, day)) + } + 7 => { + let (ch, year) = le_u16(ch)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + let (ch, hour) = be_u8(ch)?; + let (ch, minute) = be_u8(ch)?; + let (ch, second) = be_u8(ch)?; + ( + ch, + format!( + "datetime {:04}-{:02}-{:02} {:02}:{:02}:{:02}", + year, month, day, hour, minute, second + ), + ) + } + 11 => { + let (ch, year) = le_u16(ch)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + let (ch, hour) = be_u8(ch)?; + let (ch, minute) = be_u8(ch)?; + let (ch, second) = be_u8(ch)?; + let (ch, microsecond) = le_u32(ch)?; + ( + ch, + format!( + "datetime {:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}", + year, month, day, hour, minute, second, microsecond, + ), + ) + } + _ => { + let (ch, _) = take(len)(ch)?; + (ch, "".to_string()) + } + } + } + _ => (ch, "".to_string()), + }; + params.push(res); + data = ch; + } + Ok(( + i, + MysqlCommand::StmtExecute { + statement_id, + params: Some(params), + }, + )) + } else { + Ok(( + i, + MysqlCommand::StmtExecute { + statement_id, + params: None, + }, + )) + } + } else { + Ok(( + i, + MysqlCommand::StmtExecute { + statement_id, + params: None, + }, + )) + } + } else { + let consumed = (old.len() - i.len()) as u32; + // Should never happen + if consumed > length { + return Ok(( + &[], + MysqlCommand::StmtExecute { + statement_id, + params: None, + }, + )); + } + let (i, _) = take(length - consumed as u32)(i)?; + Ok(( + i, + MysqlCommand::StmtExecute { + statement_id, + params: None, + }, + )) + } +} + +fn parse_field_list_cmd(i: &[u8], length: u32) -> IResult<&[u8], MysqlCommand> { + let old = i; + let (i, table) = map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let consumed = (old.len() - i.len()) as u32; + // Should never happen + if consumed > length { + return Ok(( + &[], + MysqlCommand::FieldList { + table: "".to_string(), + }, + )); + } + let (i, _) = take(length - consumed)(i)?; + Ok((i, MysqlCommand::FieldList { table })) +} + +fn parse_stmt_fetch_cmd(i: &[u8]) -> IResult<&[u8], MysqlCommand> { + let (i, statement_id) = le_u32(i)?; + let (i, number_rows) = le_u32(i)?; + Ok(( + i, + MysqlCommand::StmtFetch { + statement_id, + number_rows, + }, + )) +} + +fn parse_stmt_close_cmd(i: &[u8]) -> IResult<&[u8], MysqlCommand> { + let (i, statement_id) = le_u32(i)?; + Ok((i, MysqlCommand::StmtClose { statement_id })) +} + +fn parse_column_definition(i: &[u8]) -> IResult<&[u8], MysqlColumnDefinition> { + let (i, _header) = parse_packet_header(i)?; + let (i, _len) = parse_varint(i)?; + let (i, _catalog) = map(take(_len as u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, _len) = parse_varint(i)?; + let (i, schema) = map(take(_len as u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, _len) = parse_varint(i)?; + let (i, table) = map(take(_len as u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, _len) = parse_varint(i)?; + let (i, orig_table) = map(take(_len as u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, _len) = parse_varint(i)?; + let (i, name) = map(take(_len as u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, _len) = parse_varint(i)?; + let (i, _orig_name) = map(take(_len as u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, _) = parse_varint(i)?; + let (i, character_set) = le_u16(i)?; + let (i, column_length) = le_u32(i)?; + let (i, field_type) = be_u8(i)?; + let (i, flags) = le_u16(i)?; + let (i, decimals) = be_u8(i)?; + let (i, _filter) = take(2_u32)(i)?; + + let field_type = field_type.into(); + + Ok(( + i, + MysqlColumnDefinition { + catalog: "def".to_string(), + schema, + table, + orig_table, + name, + character_set, + column_length, + field_type, + flags, + decimals, + }, + )) +} + +fn parse_resultset_row_texts(i: &[u8], mut length: u32) -> IResult<&[u8], Vec> { + let mut rem = i; + let mut texts = Vec::new(); + while length > 0 { + let (i, len) = parse_varint(rem)?; + let mut consumed = (rem.len() - i.len()) as u32; + if len == 0xFB { + texts.push("NULL".to_string()); + rem = i; + } else { + let (i, text) = map(take(len), |s: &[u8]| String::from_utf8_lossy(s).to_string())(i)?; + texts.push(text); + consumed += len as u32; + rem = i; + } + // Should never happen + if consumed > length { + return Ok((&[], texts)); + } + length -= consumed; + } + + Ok((rem, texts)) +} + +fn parse_resultset_row(i: &[u8]) -> IResult<&[u8], MysqlResultSetRow> { + let (i, header) = parse_packet_header(i)?; + let (i, texts) = parse_resultset_row_texts(i, header.pkt_len)?; + + Ok((i, MysqlResultSetRow { texts })) +} + +fn parse_binary_resultset_row( + columns: Vec, +) -> impl FnMut(&[u8]) -> IResult<&[u8], MysqlResultBinarySetRow> { + move |i| { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = verify(be_u8, |&x| x == 0x00 || x == 0xFF)(i)?; + // ERR + if response_code == 0xFF { + let (i, _resp) = parse_response_err(i, header.pkt_len - 1)?; + return Ok((i, MysqlResultBinarySetRow::Err)); + } + let (i, data) = take(header.pkt_len - 1)(i)?; + + // NULL-bitmap, [(column-count + 7 + 2) / 8 bytes] + let mut texts = Vec::new(); + let mut pos = (columns.len() + 7 + 2) >> 3; + let null_mask = &data[..pos]; + for i in 0..columns.len() { + // Field is NULL + // byte = ((field-pos + 2) / 8) + // bit-pos = ((field-pos + 2) % 8) + // (byte >> bit-pos) % 2 == 1 + if ((null_mask[(i + 2) >> 3] >> ((i + 2) & 7)) & 1) == 1 { + continue; + } + + match columns[i].field_type { + FieldType::NULL => texts.push("NULL".to_string()), + FieldType::Tiny => { + if columns[i].flags & (FIELD_FLAGS_UNSIGNED as u16) != 0 { + texts.push(format!("{}", data[pos].to_u8().unwrap_or_default())); + } else { + texts.push(format!("{}", data[pos].to_i8().unwrap_or_default())); + } + pos += 1; + } + FieldType::Short | FieldType::Year => { + if columns[i].flags & (FIELD_FLAGS_UNSIGNED as u16) != 0 { + texts.push(format!( + "{}", + u16::from_le_bytes(data[pos..pos + 2].try_into().unwrap_or_default()) + )); + } else { + texts.push(format!( + "{}", + i16::from_le_bytes(data[pos..pos + 2].try_into().unwrap_or_default()) + )); + } + pos += 2; + } + FieldType::Int24 | FieldType::Long => { + if columns[i].flags & (FIELD_FLAGS_UNSIGNED as u16) != 0 { + texts.push(format!( + "{}", + u32::from_le_bytes(data[pos..pos + 4].try_into().unwrap_or_default()) + )); + } else { + texts.push(format!( + "{}", + i32::from_le_bytes(data[pos..pos + 4].try_into().unwrap_or_default()) + )); + } + pos += 4; + } + FieldType::LongLong => { + if columns[i].flags & (FIELD_FLAGS_UNSIGNED as u16) != 0 { + texts.push(format!( + "{}", + u64::from_le_bytes(data[pos..pos + 8].try_into().unwrap_or_default()) + )); + } else { + texts.push(format!( + "{}", + i64::from_le_bytes(data[pos..pos + 8].try_into().unwrap_or_default()) + )); + } + pos += 8; + } + FieldType::Float => { + texts.push(format!( + "{}", + f32::from_le_bytes(data[pos..pos + 4].try_into().unwrap_or_default()) + )); + pos += 4; + } + FieldType::Double => { + texts.push(format!( + "{}", + f64::from_le_bytes(data[pos..pos + 8].try_into().unwrap_or_default()) + )); + pos += 8; + } + FieldType::Decimal + | FieldType::NewDecimal + | FieldType::Varchar + | FieldType::Bit + | FieldType::Enum + | FieldType::Set + | FieldType::TinyBlob + | FieldType::MediumBlob + | FieldType::LongBlob + | FieldType::Blob + | FieldType::VarString + | FieldType::String + | FieldType::Geometry + | FieldType::Json + | FieldType::Vector => { + let length_string = &data[pos..]; + let (not_readed, length) = parse_varint(length_string)?; + if length_string.len() < length as usize { + break; + } + pos += length_string.len() - not_readed.len(); + if length > 0 { + let (_, string) = + map(take(length), |s| String::from_utf8_lossy(s).to_string())( + not_readed, + )?; + texts.push(string); + pos += length as usize; + } + } + FieldType::Date + | FieldType::NewDate + | FieldType::Datetime + | FieldType::Datetime2 + | FieldType::Timestamp + | FieldType::Timestamp2 + | FieldType::Time + | FieldType::Time2 => { + let length_string = &data[pos..]; + let (not_readed, length) = parse_varint(length_string)?; + if length_string.len() < length as usize { + break; + } + pos += length_string.len() - not_readed.len(); + let string = match length { + 0 => "datetime 0000-00-00 00:00:00.000000".to_string(), + 4 => { + let (ch, year) = le_u16(not_readed)?; + let (ch, month) = be_u8(ch)?; + let (_, day) = be_u8(ch)?; + format!("datetime {:04}-{:02}-{:02}", year, month, day) + } + 7 => { + let (ch, year) = le_u16(not_readed)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + let (ch, hour) = be_u8(ch)?; + let (ch, minute) = be_u8(ch)?; + let (_, second) = be_u8(ch)?; + format!( + "datetime {:04}-{:02}-{:02} {:02}:{:02}:{:02}", + year, month, day, hour, minute, second + ) + } + 11 => { + let (ch, year) = le_u16(not_readed)?; + let (ch, month) = be_u8(ch)?; + let (ch, day) = be_u8(ch)?; + let (ch, hour) = be_u8(ch)?; + let (ch, minute) = be_u8(ch)?; + let (ch, second) = be_u8(ch)?; + let (_, microsecond) = le_u32(ch)?; + format!( + "datetime {:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}", + year, month, day, hour, minute, second, microsecond, + ) + } + _ => "".to_string(), + }; + pos += length as usize; + texts.push(string); + } + _ => { + break; + } + } + } + let texts = texts.join(","); + + Ok((i, MysqlResultBinarySetRow::Text(texts))) + } +} + +fn parse_response_resultset(i: &[u8], n_cols: u64) -> IResult<&[u8], MysqlResponse> { + let (i, columns) = many_m_n(n_cols as usize, n_cols as usize, parse_column_definition)(i)?; + let (i, eof) = parse_eof_packet(i)?; + let (i, (rows, _)) = many_till(parse_resultset_row, |i| { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = verify(be_u8, |&x| x == 0xFE || x == 0xFF)(i)?; + match response_code { + // EOF + 0xFE => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::EOF, + }, + )) + } + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + _ => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Unknown, + }, + )) + } + } + })(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::ResultSet { + n_cols, + columns, + eof, + rows, + }, + }, + )) +} + +fn parse_response_binary_resultset(i: &[u8], n_cols: u64) -> IResult<&[u8], MysqlResponse> { + let (i, columns) = many_m_n(n_cols as usize, n_cols as usize, parse_column_definition)(i)?; + let (i, eof) = parse_eof_packet(i)?; + let (i, (rows, _)) = many_till(parse_binary_resultset_row(columns), |i| { + // eof + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = verify(be_u8, |&x| x == 0xFE || x == 0xFF)(i)?; + match response_code { + // EOF + 0xFE => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::EOF, + }, + )) + } + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + _ => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Unknown, + }, + )) + } + } + })(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::BinaryResultSet { n_cols, eof, rows }, + }, + )) +} + +fn parse_response_ok(i: &[u8], length: u32) -> IResult<&[u8], MysqlResponse> { + let old = i; + let (i, rows) = parse_varint(i)?; + let (i, _last_insert_id) = parse_varint(i)?; + let (i, flags) = le_u16(i)?; + let (i, warnings) = le_u16(i)?; + let consumed = (old.len() - i.len()) as u32; + // Should never happen + if consumed > length { + return Ok(( + &[], + MysqlResponse { + item: MysqlResponsePacket::Ok { + rows, + flags, + warnings, + }, + }, + )); + } + let (i, _) = take(length - consumed)(i)?; + + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Ok { + rows, + flags, + warnings, + }, + }, + )) +} + +fn parse_response_err(i: &[u8], length: u32) -> IResult<&[u8], MysqlResponse> { + let (i, error_code) = le_u16(i)?; + let (i, _) = take(6_u32)(i)?; + // sql state maker & sql state + let (i, _) = take(6_u32)(i)?; + let length = length - 2 - 12; + let (i, error_message) = map(take(length), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Err { + error_code, + error_message, + }, + }, + )) +} + +pub fn parse_handshake_request(i: &[u8]) -> IResult<&[u8], MysqlHandshakeRequest> { + let (i, _header) = parse_packet_header(i)?; + let (i, protocol) = verify(be_u8, |&x| x == 0x0a_u8)(i)?; + let (i, version) = map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let (i, _) = take(1_u32)(i)?; + let (i, conn_id) = le_u32(i)?; + let (i, salt1) = map(take(8_u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let (i, _) = take(1_u32)(i)?; + let (i, capability_flags1) = le_u16(i)?; + let (i, character_set) = be_u8(i)?; + let (i, status_flags) = le_u16(i)?; + let (i, capability_flags2) = le_u16(i)?; + let (i, auth_plugin_len) = be_u8(i)?; + let (i, _) = take(10_u32)(i)?; + let (i, salt2) = map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let (i, _) = take(1_u32)(i)?; + let (i, auth_plugin_data) = cond( + auth_plugin_len > 0, + map(take(auth_plugin_len as usize), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + }), + )(i)?; + let (i, _) = take(1_u32)(i)?; + Ok(( + i, + MysqlHandshakeRequest { + protocol, + version, + conn_id, + salt1, + capability_flags1, + character_set, + status_flags, + capability_flags2, + auth_plugin_len, + salt2, + auth_plugin_data, + }, + )) +} + +pub fn parse_handshake_capabilities(i: &[u8]) -> IResult<&[u8], u32> { + let (i, _header) = parse_packet_header(i)?; + let (i, client_flags) = verify(le_u32, |&client_flags| { + client_flags & CLIENT_PROTOCOL_41 != 0 + })(i)?; + let (i, _max_packet_size) = be_u32(i)?; + let (i, _character_set) = be_u8(i)?; + + // fk this code + Ok((i, client_flags)) +} + +pub fn parse_handshake_ssl_request(i: &[u8]) -> IResult<&[u8], MysqlSSLRequest> { + let (i, filter) = map(take(23_u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + Ok(( + i, + MysqlSSLRequest { + filter: Some(filter), + }, + )) +} + +pub fn parse_handshake_response( + i: &[u8], client_flags: u32, +) -> IResult<&[u8], MysqlHandshakeResponse> { + let (i, _filter) = map(take(23_u32), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let (i, username) = map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let (i, _) = take(1_u32)(i)?; + let (i, auth_response_len) = be_u8(i)?; + let (i, auth_response) = map(take(auth_response_len as usize), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + + let (i, database) = cond( + client_flags & CLIENT_CONNECT_WITH_DB != 0, + map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + }), + )(i)?; + let (i, _) = cond(database.is_some(), take(1_u32))(i)?; + + let (i, client_plugin_name) = cond( + client_flags & CLIENT_PLUGIN_AUTH != 0, + map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + }), + )(i)?; + let (i, _) = cond(client_plugin_name.is_some(), take(1_u32))(i)?; + + let (i, length) = cond(client_flags & CLIENT_CONNECT_ATTRS != 0, be_u8)(i)?; + + let (i, attributes) = cond( + length.is_some(), + parse_handshake_response_attributes(length), + )(i)?; + + let (i, zstd_compression_level) = + cond(client_flags & CLIENT_ZSTD_COMPRESSION_ALGORITHM != 0, be_u8)(i)?; + Ok(( + i, + MysqlHandshakeResponse { + username, + auth_response_len, + auth_response, + database, + client_plugin_name, + attributes, + zstd_compression_level, + client_flags, + }, + )) +} + +fn parse_handshake_response_attributes( + length: Option, +) -> impl FnMut(&[u8]) -> IResult<&[u8], Vec> { + move |i| { + if length.is_none() { + return Ok((i, Vec::new())); + } + let mut length = length.unwrap(); + let mut res = vec![]; + let mut rem = i; + while length > 0 { + let (i, key_len) = be_u8(rem)?; + // length contains key_len + length -= 1; + let (i, key) = map(take(key_len as usize), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let (i, value_len) = be_u8(i)?; + // length contains value_len + length -= 1; + let (i, value) = map(take(value_len as usize), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + res.push(MysqlHandshakeResponseAttribute { key, value }); + length -= key_len + value_len; + rem = i; + } + + Ok((rem, res)) + } +} + +pub fn parse_auth_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, status) = be_u8(i)?; + match status { + 0x00 => { + let (i, response) = parse_response_ok(i, header.pkt_len - 1)?; + Ok((i, response)) + } + // AuthMoreData + 0x01 => { + let (i, data) = be_u8(i)?; + let (i, _) = cond(header.pkt_len - 2 > 0, take(header.pkt_len - 2))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::AuthMoreData { data }, + }, + )) + } + 0xEF => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::EOF, + }, + )) + } + _ => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Unknown, + }, + )) + } + } +} + +pub fn parse_auth_switch_request(i: &[u8]) -> IResult<&[u8], MysqlAuthSwtichRequest> { + let (i, header) = parse_packet_header(i)?; + let plugin_length = i.len(); + let (i, plugin_name) = map(take_till(|ch| ch == 0x00), |s: &[u8]| { + String::from_utf8_lossy(s).to_string() + })(i)?; + let plugin_length = plugin_length - i.len(); + let (i, plugin_data) = map( + cond( + header.pkt_len - (plugin_length as u32) > 0, + take(header.pkt_len - plugin_length as u32), + ), + |ch: Option<&[u8]>| { + if let Some(ch) = ch { + String::from_utf8_lossy(ch).to_string() + } else { + String::new() + } + }, + )(i)?; + + Ok(( + i, + MysqlAuthSwtichRequest { + plugin_name, + plugin_data, + }, + )) +} + +pub fn parse_local_file_data_content(i: &[u8]) -> IResult<&[u8], u32> { + let (i, header) = parse_packet_header(i)?; + let (i, _) = take(header.pkt_len)(i)?; + Ok((i, header.pkt_len)) +} + +pub fn parse_request( + i: &[u8], params: Option, param_types: Option>, + stmt_long_datas: Option>, client_flags: u32, +) -> IResult<&[u8], MysqlRequest> { + let (i, header) = parse_packet_header(i)?; + let (i, command_code) = be_u8(i)?; + match command_code { + 0x01 => Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::Quit, + }, + )), + + 0x02 => { + let (i, command) = parse_init_db_cmd(i, header.pkt_len - 1)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + + 0x03 => { + let (i, command) = parse_query_cmd(i, header.pkt_len - 1, client_flags)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + + 0x04 => { + let (i, command) = parse_field_list_cmd(i, header.pkt_len - 1)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + + 0x08 => Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::Statistics, + }, + )), + + 0x0D => Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::Debug, + }, + )), + + 0x0e => Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::Ping, + }, + )), + + 0x11 => { + let (i, _) = take(header.pkt_len - 1)(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::ChangeUser, + }, + )) + } + 0x1A => { + let length = header.pkt_len - 1; + if length == 2 { + let (i, _) = le_u16(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::SetOption, + }, + )) + } else if length == 4 { + let (i, statement_id) = le_u32(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::StmtReset { statement_id }, + }, + )) + } else { + Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::Unknown, + }, + )) + } + } + + 0x1F => Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::ResetConnection, + }, + )), + + 0x16 => { + let (i, command) = parse_stmt_prepare_cmd(i, header.pkt_len - 1)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + + 0x17 => { + let (i, command) = parse_stmt_execute_cmd( + i, + params, + param_types, + stmt_long_datas, + header.pkt_len - 1, + client_flags, + )?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + + 0x18 => { + let (i, command) = parse_stmt_send_long_data_cmd(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + + 0x19 => { + // + if header.pkt_len - 1 == 8 { + let (i, command) = parse_stmt_fetch_cmd(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } else { + let (i, command) = parse_stmt_close_cmd(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command, + }, + )) + } + } + + _ => { + SCLogDebug!( + "Unknown request, header: {:?}, command_code: {}", + header, + command_code + ); + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlRequest { + command_code, + command: MysqlCommand::Unknown, + }, + )) + } + } +} + +pub fn parse_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + // OK + 0x00 => parse_response_ok(i, header.pkt_len - 1), + // LOCAL INFILE Request + 0xFB => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::LocalInFileRequest, + }, + )) + } + // EOF + 0xFE => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::EOF, + }, + )) + } + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + // Text Resultset + _ => parse_response_resultset(i, response_code as u64), + } +} + +pub fn parse_change_user_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + // OK + 0x00 => parse_response_ok(i, header.pkt_len - 1), + // AuthSwitch + 0xFE => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::AuthSwithRequest, + }, + )) + } + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + _ => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Unknown, + }, + )) + } + } +} + +pub fn parse_statistics_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, _) = take(header.pkt_len)(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Statistics, + }, + )) +} + +pub fn parse_field_list_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + 0x00 => Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::FieldsList { columns: None }, + }, + )), + _ => { + let n_cols = response_code; + let (i, columns) = + many_m_n(n_cols as usize, n_cols as usize, parse_column_definition)(i)?; + let (i, _) = parse_eof_packet(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::FieldsList { + columns: Some(columns), + }, + }, + )) + } + } +} + +pub fn parse_stmt_prepare_response(i: &[u8], _client_flags: u32) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + 0x00 => { + let (i, statement_id) = le_u32(i)?; + let (i, num_columns) = le_u16(i)?; + let (i, num_params) = le_u16(i)?; + let (i, _filter) = be_u8(i)?; + //TODO: why? + // let (i, _warning_cnt) = cond(header.pkt_len > 12, take(2_u32))(i)?; + let (i, _warning_cnt) = take(2_u32)(i)?; + let (i, params) = cond( + num_params > 0, + many_till(parse_column_definition, parse_eof_packet), + )(i) + .map(|(i, params)| { + if let Some(params) = params { + (i, Some(params.0)) + } else { + (i, None) + } + })?; + let (i, fields) = cond( + num_columns > 0, + many_till(parse_column_definition, parse_eof_packet), + )(i) + .map(|(i, fields)| { + if let Some(fields) = fields { + (i, Some(fields.0)) + } else { + (i, None) + } + })?; + + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::StmtPrepare { + statement_id, + num_params, + params, + fields, + }, + }, + )) + } + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + _ => Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::Unknown, + }, + )), + } +} + +pub fn parse_stmt_execute_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + 0x00 => parse_response_ok(i, header.pkt_len - 1), + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + _ => parse_response_binary_resultset(i, response_code as u64), + } +} + +pub fn parse_stmt_fetch_response(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + 0x00 => parse_response_ok(i, header.pkt_len - 1), + // ERR + 0xFF => parse_response_err(i, header.pkt_len - 1), + _ => parse_response_binary_resultset(i, response_code as u64), + } +} + +pub fn parse_auth_request(i: &[u8]) -> IResult<&[u8], ()> { + let (i, header) = parse_packet_header(i)?; + let (i, _) = cond(header.pkt_len > 0, take(header.pkt_len))(i)?; + Ok((i, ())) +} + +pub fn parse_auth_responsev2(i: &[u8]) -> IResult<&[u8], MysqlResponse> { + let (i, header) = parse_packet_header(i)?; + let (i, response_code) = be_u8(i)?; + match response_code { + // OK + 0x00 => parse_response_ok(i, header.pkt_len - 1), + // auth data + _ => { + let (i, _) = cond(header.pkt_len - 1 > 0, take(header.pkt_len - 1))(i)?; + Ok(( + i, + MysqlResponse { + item: MysqlResponsePacket::AuthData, + }, + )) + } + } +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn test_parse_packet_header() { + let pkt: &[u8] = &[0x07, 0x00, 0x00, 0x03]; + let (rem, packet_header) = parse_packet_header(pkt).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(packet_header.pkt_len, 7); + assert_eq!(packet_header.pkt_num, 3); + } + + #[test] + fn test_parse_handshake_request() { + let pkt: &[u8] = &[ + 0x49, 0x00, 0x00, 0x00, 0x0a, 0x38, 0x2e, 0x34, 0x2e, 0x30, 0x00, 0x51, 0x00, 0x00, + 0x00, 0x3e, 0x7d, 0x6a, 0x6a, 0x1a, 0x2d, 0x2b, 0x6b, 0x00, 0xff, 0xff, 0xff, 0x02, + 0x00, 0xff, 0xdf, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x74, 0x54, 0x07, 0x62, 0x28, 0x5d, 0x21, 0x06, 0x44, 0x06, 0x62, 0x00, 0x63, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x5f, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00, + ]; + let (rem, handshake_request) = parse_handshake_request(pkt).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(handshake_request.protocol, 10); + assert_eq!(handshake_request.version, "8.4.0"); + assert_eq!(handshake_request.conn_id, 81); + assert_eq!(handshake_request.capability_flags1, 0xffff); + assert_eq!(handshake_request.status_flags, 0x0002); + assert_eq!(handshake_request.capability_flags2, 0xdfff); + assert_eq!(handshake_request.auth_plugin_len, 21); + assert_eq!( + handshake_request.auth_plugin_data, + Some("caching_sha2_password".to_string()), + ); + let pkt: &[u8] = &[ + 0x49, 0x00, 0x00, 0x00, 0x0a, 0x39, 0x2e, 0x30, 0x2e, 0x31, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x5e, 0x09, 0x7c, 0x41, 0x76, 0x5d, 0x66, 0x17, 0x00, 0xff, 0xff, 0xff, 0x02, + 0x00, 0xff, 0xdf, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x7a, 0x03, 0x13, 0x35, 0x71, 0x0a, 0x4e, 0x2f, 0x45, 0x34, 0x00, 0x63, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x5f, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00, + ]; + let (rem, _) = parse_handshake_request(pkt).unwrap(); + + assert!(rem.is_empty()); + } + + #[test] + fn test_parse_handshake_response() { + let pkt: &[u8] = &[ + 0xc6, 0x00, 0x00, 0x01, 0x8d, 0xa2, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x20, + 0xbd, 0xb9, 0xfd, 0xe3, 0x22, 0xce, 0x86, 0x7d, 0x6c, 0x1d, 0x0e, 0xad, 0x22, 0x92, + 0xde, 0x56, 0xe5, 0xf2, 0x3d, 0xf8, 0xe0, 0x1f, 0x6f, 0x59, 0x5e, 0x62, 0xa6, 0x6b, + 0x7e, 0x54, 0x61, 0xfc, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x2d, 0x66, + 0x6c, 0x6f, 0x77, 0x00, 0x63, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, + 0x61, 0x32, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00, 0x5b, 0x0c, + 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x0f, 0x47, + 0x6f, 0x2d, 0x4d, 0x79, 0x53, 0x51, 0x4c, 0x2d, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, + 0x03, 0x5f, 0x6f, 0x73, 0x05, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x09, 0x5f, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x05, 0x61, 0x6d, 0x64, 0x36, 0x34, 0x04, 0x5f, + 0x70, 0x69, 0x64, 0x06, 0x34, 0x35, 0x30, 0x39, 0x37, 0x36, 0x0c, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x0a, 0x31, 0x37, 0x32, 0x2e, + 0x31, 0x37, 0x2e, 0x30, 0x2e, 0x32, + ]; + + let (rem, client_flags) = parse_handshake_capabilities(pkt).unwrap(); + let (rem, handshake_response) = parse_handshake_response(rem, client_flags).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(handshake_response.username, "root"); + assert_eq!( + handshake_response.database, + Some("sentinel-flow".to_string()) + ); + assert_eq!( + handshake_response.client_plugin_name, + Some("caching_sha2_password".to_string()) + ); + let pkt: &[u8] = &[ + 0x5c, 0x00, 0x00, 0x01, 0x85, 0xa2, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x20, + 0x9f, 0xbd, 0x98, 0xd7, 0x8f, 0x7b, 0x74, 0xfe, 0x9e, 0x4e, 0x99, 0x64, 0xc0, 0xd0, + 0x6a, 0x1d, 0x56, 0xbf, 0x36, 0xb1, 0xcd, 0x10, 0x6d, 0x3a, 0x37, 0xaf, 0x25, 0x22, + 0x06, 0xb6, 0xe5, 0x13, 0x63, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, + 0x61, 0x32, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00, + ]; + let (rem, client_flags) = parse_handshake_capabilities(pkt).unwrap(); + let (rem, _) = parse_handshake_response(rem, client_flags).unwrap(); + + assert!(rem.is_empty()); + } + + #[test] + fn test_parse_auth_response() { + let pkt: &[u8] = &[0x02, 0x00, 0x00, 0x02, 0x01, 0x03]; + + let (rem, auth_response) = parse_auth_response(pkt).unwrap(); + + assert!(rem.is_empty()); + let item = auth_response.item; + if let MysqlResponsePacket::AuthMoreData { data } = item { + assert_eq!(data, SHA2_PASSWORD_FAST_AUTH_SUCCESS); + } else { + unreachable!(); + } + } + + //TODO: test auth switch + #[test] + fn test_parse_auth_switch_request() {} + + #[test] + fn test_parse_query_request() { + let pkt: &[u8] = &[ + 0x12, 0x00, 0x00, 0x00, 0x03, 0x53, 0x45, 0x54, 0x20, 0x4e, 0x41, 0x4d, 0x45, 0x53, + 0x20, 0x75, 0x74, 0x66, 0x38, 0x6d, 0x62, 0x34, + ]; + + let (rem, request) = parse_request(pkt, None, None, None, 0).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(request.command_code, 0x03); + + let command = request.command; + if let MysqlCommand::Query { query } = command { + assert_eq!(query, "SET NAMES utf8mb4".to_string()); + } else { + unreachable!(); + } + } + + #[test] + fn test_parse_ok_response() { + let pkt: &[u8] = &[ + 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + ]; + + let (rem, response) = parse_response(pkt).unwrap(); + assert!(rem.is_empty()); + + let item = response.item; + if let MysqlResponsePacket::Ok { + rows, + flags, + warnings, + } = item + { + assert_eq!(rows, 0); + assert_eq!(flags, 0x0002); + assert_eq!(warnings, 0); + } else { + unreachable!(); + } + let pkt: &[u8] = &[ + 0x30, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x52, 0x6f, + 0x77, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x3a, 0x20, 0x31, 0x20, + 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x3a, 0x20, 0x31, 0x20, 0x20, 0x57, + 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x20, 0x30, + ]; + + let (rem, _) = parse_response(pkt).unwrap(); + assert!(rem.is_empty()); + + let pkt: &[u8] = &[ + 0x30, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x52, 0x6f, + 0x77, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x3a, 0x20, 0x31, 0x20, + 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x3a, 0x20, 0x31, 0x20, 0x20, 0x57, + 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x20, 0x30, + ]; + let (rem, _) = parse_response(pkt).unwrap(); + assert!(rem.is_empty()); + } + + //TODO: test response error + #[test] + fn test_parse_err_response() { + let pkt: &[u8] = &[ + 51, 0, 0, 0, 255, 134, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 97, 110, 32, 101, + 114, 114, 111, 114, 32, 114, 101, 97, 100, 105, 110, 103, 32, 99, 111, 109, 109, 117, + 110, 105, 99, 97, 116, 105, 111, 110, 32, 112, 97, 99, 107, 101, 116, 115, + ]; + let (rem, _) = parse_response(pkt).unwrap(); + assert!(rem.is_empty()); + } + + #[test] + fn test_parse_resultset_response() { + let pkt: &[u8] = &[ + 0x01, 0x00, 0x00, 0x01, 0x01, 0x1f, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, + 0x00, 0x00, 0x09, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x28, 0x29, 0x00, 0x0c, + 0xff, 0x00, 0x14, 0x00, 0x00, 0x00, 0xfd, 0x01, 0x00, 0x1f, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x03, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x04, 0x05, 0x39, 0x2e, + 0x30, 0x2e, 0x31, 0x05, 0x00, 0x00, 0x05, 0xfe, 0x00, 0x00, 0x02, 0x00, + ]; + + let (rem, response) = parse_response(pkt).unwrap(); + assert!(rem.is_empty()); + + let item = response.item; + if let MysqlResponsePacket::ResultSet { + n_cols, + columns: _, + eof: _, + rows: _, + } = item + { + assert_eq!(n_cols, 1); + } else { + unreachable!(); + } + + let pkt: &[u8] = &[ + 0x01, 0x00, 0x00, 0x01, 0x01, 0x4d, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x09, 0x73, 0x79, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, + 0x73, 0x09, 0x73, 0x79, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x09, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x0c, 0xff, 0x00, 0xfc, 0x02, 0x00, 0x00, 0xfd, 0x0c, 0x40, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, + 0x04, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x07, 0x00, 0x00, 0x05, 0x06, 0x73, 0x75, + 0x6e, 0x6a, 0x69, 0x65, 0x04, 0x00, 0x00, 0x06, 0x03, 0x6c, 0x77, 0x77, 0x08, 0x00, + 0x00, 0x07, 0x07, 0x6b, 0x61, 0x72, 0x65, 0x6b, 0x69, 0x69, 0x03, 0x00, 0x00, 0x08, + 0x02, 0x6d, 0x75, 0x04, 0x00, 0x00, 0x09, 0x03, 0x6c, 0x64, 0x62, 0x07, 0x00, 0x00, + 0x0a, 0x06, 0x71, 0x69, 0x75, 0x71, 0x69, 0x75, 0x0c, 0x00, 0x00, 0x0b, 0x0b, 0x44, + 0x6f, 0x6e, 0x67, 0x77, 0x65, 0x6e, 0x74, 0x6f, 0x6e, 0x67, 0x08, 0x00, 0x00, 0x0c, + 0x07, 0x4b, 0x65, 0x79, 0x75, 0x72, 0x75, 0x69, 0x0b, 0x00, 0x00, 0x0d, 0x0a, 0x31, + 0x32, 0x40, 0x20, 0xe8, 0x90, 0xa8, 0xe8, 0xbe, 0xbe, 0x16, 0x00, 0x00, 0x0e, 0x15, + 0xe8, 0xbf, 0x99, 0xe6, 0x98, 0xaf, 0xe4, 0xb8, 0x80, 0xe4, 0xb8, 0xaa, 0xe7, 0x94, + 0xa8, 0xe6, 0x88, 0xb7, 0xe5, 0x90, 0x8d, 0x05, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, + 0x02, 0x00, + ]; + + let (rem, _) = parse_response(pkt).unwrap(); + assert!(rem.is_empty()); + + let pkt: &[u8] = &[ + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, + 0x3f, 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x03, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, + 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x04, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x6f, 0x00, 0x00, 0x05, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x13, 0x73, 0x79, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6e, 0x75, + 0x73, 0x13, 0x73, 0x79, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x73, 0x10, 0x73, 0x79, 0x73, 0x5f, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x5f, 0x69, 0x64, 0x10, 0x73, 0x79, 0x73, + 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x5f, 0x69, 0x64, 0x0c, + 0x3f, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x23, 0x50, 0x00, 0x00, 0x00, 0x83, 0x00, + 0x00, 0x06, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x13, 0x73, + 0x79, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, + 0x65, 0x6e, 0x75, 0x73, 0x13, 0x73, 0x79, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x73, 0x1a, 0x73, 0x79, 0x73, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x1a, 0x73, 0x79, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x08, 0x23, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, + 0x02, 0x00, + ]; + + let (rem, _response) = parse_stmt_prepare_response(pkt, 0).unwrap(); + assert!(rem.is_empty()); + } + + #[test] + fn test_parse_quit_request() { + let pkt: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x01]; + + let (rem, request) = parse_request(pkt, None, None, None, 0).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(request.command_code, 0x01); + + let command = request.command; + if let MysqlCommand::Quit = command { + } else { + unreachable!(); + } + } + + #[test] + fn test_parse_prepare_stmt_request() { + let pkt: &[u8] = &[ + 0x2b, 0x00, 0x00, 0x00, 0x16, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x2a, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x20, + 0x57, 0x48, 0x45, 0x52, 0x45, 0x20, 0x69, 0x64, 0x20, 0x3d, 0x3f, 0x20, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x20, 0x31, + ]; + + let (rem, request) = parse_request(pkt, None, None, None, 0).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(request.command_code, 0x16); + + let command = request.command; + + if let MysqlCommand::StmtPrepare { query } = command { + assert_eq!(query, "select * from requests WHERE id =? limit 1"); + } else { + unreachable!(); + } + let pkt: &[u8] = &[ + 64, 0, 0, 0, 22, 83, 69, 76, 69, 67, 84, 32, 96, 114, 101, 115, 111, 117, 114, 99, 101, + 96, 32, 70, 82, 79, 77, 32, 96, 115, 121, 115, 95, 97, 117, 116, 104, 111, 114, 105, + 116, 105, 101, 115, 96, 32, 87, 72, 69, 82, 69, 32, 97, 117, 116, 104, 111, 114, 105, + 116, 121, 95, 105, 100, 32, 61, 32, 63, + ]; + let (rem, request) = parse_request(pkt, None, None, None, 0).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(request.command_code, 0x16); + } + + #[test] + fn test_parse_prepare_stmt_response() { + let pkt: &[u8] = &[ + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, + 0x3f, 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x26, 0x00, 0x00, 0x04, + 0x03, 0x64, 0x65, 0x66, 0x04, 0x74, 0x65, 0x73, 0x74, 0x04, 0x74, 0x65, 0x73, 0x74, + 0x04, 0x74, 0x65, 0x73, 0x74, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64, 0x0c, 0x3f, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x03, 0x23, 0x42, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x05, + 0x03, 0x64, 0x65, 0x66, 0x04, 0x74, 0x65, 0x73, 0x74, 0x04, 0x74, 0x65, 0x73, 0x74, + 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, + 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x0c, 0xff, 0x00, 0xfc, 0x03, + 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x06, 0xfe, 0x00, + 0x00, 0x02, 0x00, + ]; + + let (rem, response) = parse_stmt_prepare_response(pkt, 0).unwrap(); + assert!(rem.is_empty()); + + let item = response.item; + if let MysqlResponsePacket::StmtPrepare { + statement_id, + num_params, + .. + } = item + { + assert_eq!(statement_id, 1); + assert_eq!(num_params, 1); + } else { + unreachable!(); + } + let pkt: &[u8] = &[ + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x6e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, + 0x3f, 0x00, 0x0c, 0xff, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x03, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, + 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x04, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, + 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x05, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x0c, + 0xff, 0x00, 0xfc, 0xff, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x06, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x0c, 0xff, + 0x00, 0xfc, 0xff, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x07, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x0c, 0xff, 0x00, + 0xfc, 0xff, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x17, 0x00, 0x00, 0x08, + 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x0c, 0xff, 0x00, 0xfc, + 0xff, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x17, 0x00, 0x00, 0x09, 0x03, + 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x0a, 0x03, 0x64, + 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, + 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x0b, 0xfe, 0x00, 0x00, + 0x03, 0x00, + ]; + + let (rem, _response) = parse_stmt_prepare_response(pkt, 0).unwrap(); + assert!(rem.is_empty()); + + let pkt: &[u8] = &[ + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, + 0x3f, 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x17, 0x00, 0x00, 0x03, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, 0x3f, + 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x04, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x6f, 0x00, 0x00, 0x05, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x13, 0x73, 0x79, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6e, 0x75, + 0x73, 0x13, 0x73, 0x79, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x73, 0x10, 0x73, 0x79, 0x73, 0x5f, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x5f, 0x69, 0x64, 0x10, 0x73, 0x79, 0x73, + 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x5f, 0x69, 0x64, 0x0c, + 0x3f, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x23, 0x50, 0x00, 0x00, 0x00, 0x83, 0x00, + 0x00, 0x06, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x13, 0x73, + 0x79, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, + 0x65, 0x6e, 0x75, 0x73, 0x13, 0x73, 0x79, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x65, 0x6e, 0x75, 0x73, 0x1a, 0x73, 0x79, 0x73, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x61, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x1a, 0x73, 0x79, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x08, 0x23, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, + 0x02, 0x00, + ]; + + let (rem, _response) = parse_stmt_prepare_response(pkt, 0).unwrap(); + assert!(rem.is_empty()); + + let pkt: &[u8] = &[ + 0x0c, 0x00, 0x00, 0x01, 0x00, 0xf3, 0x01, 0x00, 0x00, 0x29, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x00, 0x00, 0x00, 0x01, + 0x3f, 0x00, 0x0c, 0x3f, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x02, 0x00, 0x4f, 0x00, 0x00, 0x04, + 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x08, 0x07, 0x42, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x05, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0c, 0x3f, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x06, + 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0c, 0x3f, + 0x00, 0x13, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, + 0x07, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0c, + 0x3f, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0c, 0x88, 0x40, 0x00, 0x00, 0x00, 0x53, 0x00, + 0x00, 0x08, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x04, 0x75, 0x75, 0x69, 0x64, 0x04, 0x75, 0x75, + 0x69, 0x64, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x09, 0x50, 0x00, 0x00, + 0x00, 0x53, 0x00, 0x00, 0x09, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, + 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, + 0x10, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x0a, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, + 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x08, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x64, 0x08, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, + 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x21, 0x10, 0x00, 0x00, + 0x00, 0x61, 0x00, 0x00, 0x0b, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, + 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0b, 0x70, 0x72, 0x6f, 0x64, + 0x75, 0x63, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x21, + 0x10, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x0c, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, + 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x03, 0x74, + 0x61, 0x67, 0x03, 0x74, 0x61, 0x67, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, + 0x01, 0x10, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x0d, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x0c, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x91, 0x10, 0x00, + 0x00, 0x00, 0x55, 0x00, 0x00, 0x0e, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, + 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, + 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x05, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x0c, 0x3f, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x03, 0x21, 0x10, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x0f, 0x03, 0x64, 0x65, + 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x0c, + 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x21, 0x10, 0x00, 0x00, 0x00, 0x53, 0x00, + 0x00, 0x10, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x04, 0x70, 0x6f, + 0x72, 0x74, 0x0c, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x21, 0x10, 0x00, 0x00, + 0x00, 0x4f, 0x00, 0x00, 0x11, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, + 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x02, 0x69, 0x70, 0x02, 0x69, + 0x70, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x00, 0x12, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, + 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, + 0x69, 0x74, 0x68, 0x6d, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, + 0x0c, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x91, 0x10, 0x00, 0x00, 0x00, 0x5d, + 0x00, 0x00, 0x13, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, + 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, + 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x69, 0x64, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x64, 0x0c, + 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, + 0x00, 0x14, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, + 0x00, 0x5b, 0x00, 0x00, 0x15, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, + 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x08, 0x6f, 0x72, 0x67, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x08, 0x6f, 0x72, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x0c, + 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, 0x00, 0x5d, 0x00, + 0x00, 0x16, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x09, 0x6f, 0x72, 0x67, 0x5f, 0x73, 0x74, 0x61, + 0x66, 0x66, 0x09, 0x6f, 0x72, 0x67, 0x5f, 0x73, 0x74, 0x61, 0x66, 0x66, 0x0c, 0xff, + 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, + 0x17, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0b, 0x73, 0x74, 0x61, 0x66, 0x66, 0x5f, 0x70, 0x68, + 0x6f, 0x6e, 0x65, 0x0b, 0x73, 0x74, 0x61, 0x66, 0x66, 0x5f, 0x70, 0x68, 0x6f, 0x6e, + 0x65, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x01, 0x10, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x18, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, + 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, + 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x19, 0x03, 0x64, 0x65, + 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x0b, 0x6f, 0x72, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0b, + 0x6f, 0x72, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0c, 0xff, 0x00, + 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x1a, + 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x63, 0x6f, 0x64, 0x65, 0x08, + 0x70, 0x6f, 0x73, 0x74, 0x63, 0x6f, 0x64, 0x65, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, + 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x1b, 0x03, 0x64, 0x65, + 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, + 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x0a, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x67, + 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x0c, 0xff, 0x00, 0xfc, 0x03, + 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x1c, 0x03, 0x64, + 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x08, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x1d, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x64, 0x65, 0x70, 0x61, 0x72, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x64, 0x65, 0x70, + 0x61, 0x72, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x1e, 0x03, 0x64, 0x65, 0x66, + 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, + 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x0c, 0x6f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0c, + 0x6f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0c, 0xff, + 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, + 0x1f, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x05, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x57, 0x00, 0x00, 0x20, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, + 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, + 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x06, 0x63, 0x68, 0x61, + 0x72, 0x67, 0x65, 0x06, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x0c, 0xff, 0x00, 0xfc, + 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x21, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x04, 0x70, 0x61, 0x72, 0x74, 0x04, 0x70, 0x61, 0x72, 0x74, 0x0c, + 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, + 0x00, 0x22, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x06, + 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x23, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0c, 0xff, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x24, 0x03, 0x64, + 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x03, 0x75, 0x72, 0x6c, 0x03, 0x75, 0x72, 0x6c, 0x0c, 0xff, 0x00, 0xfc, + 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x25, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x03, 0x6e, 0x65, 0x74, 0x03, 0x6e, 0x65, 0x74, 0x0c, 0x3f, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xf5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x26, + 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x09, 0x70, 0x61, 0x73, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x09, 0x70, 0x61, 0x73, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x0c, 0x3f, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x08, 0x21, 0x10, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x27, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x07, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x07, 0x61, 0x70, + 0x70, 0x72, 0x6f, 0x76, 0x65, 0x0c, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x21, + 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x28, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, + 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, + 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x07, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x21, 0x10, 0x00, 0x00, 0x00, 0x59, + 0x00, 0x00, 0x29, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, + 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, + 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x0c, 0xff, 0x00, 0xfc, 0x03, + 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x2a, 0x03, 0x64, + 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x0c, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x00, 0x00, 0x2b, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, + 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x06, 0x61, 0x70, 0x70, 0x5f, + 0x69, 0x64, 0x06, 0x61, 0x70, 0x70, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x2c, 0x03, 0x64, + 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x11, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x11, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x0c, 0x3f, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x2d, 0xfe, 0x00, 0x00, 0x02, 0x00, + ]; + + let (rem, _response) = parse_stmt_prepare_response(pkt, 0).unwrap(); + assert!(rem.is_empty()); + } + + // #[test] + // let pkt: &[u8] = &[ + // 22, 0, 0, 0, 23, 42, 2, 0, 0, 0, 1, 0, 0, 0, 0, 1, 8, 128, 16, 39, 0, 0, 0, 0, 0, 0, 5, + // 0, 0, 0, 25, 42, 2, 0, 0, 194, 0, 0, 0, 22, 83, 69, 76, 69, 67, 84, 32, 96, 117, 117, + // 105, 100, 96, 44, 96, 112, 97, 114, 101, 110, 116, 95, 105, 100, 96, 44, 96, 112, 97, + // 116, 104, 96, 44, 96, 110, 97, 109, 101, 96, 44, 96, 109, 101, 110, 117, 95, 108, 101, + // 118, 101, 108, 96, 44, 96, 104, 105, 100, 100, 101, 110, 96, 44, 96, 115, 111, 114, + // 116, 96, 32, 70, 82, 79, 77, 32, 96, 115, 121, 115, 95, 98, 97, 115, 101, 95, 109, 101, + // 110, 117, 115, 96, 32, 87, 72, 69, 82, 69, 32, 96, 115, 121, 115, 95, 98, 97, 115, 101, + // 95, 109, 101, 110, 117, 115, 96, 46, 96, 117, 117, 105, 100, 96, 32, 73, 78, 32, 40, + // 63, 44, 63, 44, 63, 44, 63, 41, 32, 65, 78, 68, 32, 96, 115, 121, 115, 95, 98, 97, 115, + // 101, 95, 109, 101, 110, 117, 115, 96, 46, 96, 100, 101, 108, 101, 116, 101, 100, 95, + // 97, 116, 96, 32, 73, 83, 32, 78, 85, 76, 76, 32, 79, 82, 68, 69, 82, 32, 66, 89, 32, + // 115, 111, 114, 116, 32, 97, 115, 99, + // + // + // let (rem, _request) = parse_request(pkt, Some(9), None, None, 1811085).unwrap(); + // assert!(rem.is_empty()); + // } + + // #[test] + // fn test_parse_execute_stmt_request() { + // let pkt: &[u8] = &[ + // 0x16, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // ]; + + // let (rem, request) = parse_request(pkt, Some(1), None, 0).unwrap(); + + // assert!(rem.is_empty()); + // assert_eq!(request.command_code, 0x17); + + // let command = request.command; + + // if let MysqlCommand::StmtExecute { + // statement_id, + // params, + // } = command + // { + // assert_eq!(statement_id, 1); + // assert_ne!(params, None); + + // let params = params.unwrap(); + // assert_eq!(params.len(), 1); + + // let mut params = params.iter(); + // assert_eq!(params.next(), Some(&"0".to_string())); + // } else { + // unreachable!(); + // } + + // let pkt: &[u8] = &[ + // 0x7c, 0x00, 0x00, 0x00, 0x17, 0x71, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x01, 0xfe, 0x00, 0x08, 0x00, 0x08, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, + // 0x00, 0xfe, 0x00, 0x08, 0x80, 0x08, 0x80, 0x17, 0x32, 0x30, 0x32, 0x34, 0x2d, 0x30, + // 0x39, 0x2d, 0x30, 0x34, 0x20, 0x31, 0x30, 0x3a, 0x31, 0x30, 0x3a, 0x31, 0x35, 0x2e, + // 0x38, 0x34, 0x35, 0x87, 0xc1, 0xd7, 0x66, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, + // 0x38, 0x2e, 0x32, 0x37, 0x07, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x0a, 0x57, + // 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x31, 0x30, 0x04, 0x45, 0x64, 0x67, 0x65, + // 0x69, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x75, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, + // ]; + + // let (rem, request) = parse_request(pkt, Some(9), None, 0).unwrap(); + + // assert!(rem.is_empty()); + // assert_eq!(request.command_code, 0x17); + + // let pkt: &[u8] = &[ + // 0x20, 0x00, 0x00, 0x00, 0x17, 0x70, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // 0x00, 0x01, 0x08, 0x80, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // ]; + + // let (rem, request) = parse_request(pkt, Some(2), None, 0).unwrap(); + + // assert!(rem.is_empty()); + // assert_eq!(request.command_code, 0x17); + + // // let pkt: &[u8] = &[ + // // 32, 0, 0, 0, 23, 110, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 8, 128, 8, 0, 2, 145, 1, 0, 0, 0, + // // 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 25, 110, 0, 0, 0, 18, 0, 0, 0, 3, 83, 84, 65, + // // 82, 84, 32, 84, 82, 65, 78, 83, 65, 67, 84, 73, 79, 78, + // // ]; + // // let (rem, request) = parse_request(pkt, Some(23), None, CLIENT_QUERY_ATTRIBUTES).unwrap(); + + // // assert!(rem.is_empty()); + // } + + #[test] + fn test_parse_execute_stmt_response() { + let pkt: &[u8] = &[ + 1, 0, 0, 1, 30, 65, 0, 0, 2, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, + 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, + 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 2, 105, 100, + 2, 105, 100, 12, 63, 0, 20, 0, 0, 0, 8, 35, 66, 0, 0, 0, 69, 0, 0, 3, 3, 100, 101, 102, + 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, + 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, + 101, 114, 95, 97, 112, 112, 4, 110, 97, 109, 101, 4, 110, 97, 109, 101, 12, 255, 0, + 252, 3, 0, 0, 253, 9, 80, 0, 0, 0, 69, 0, 0, 4, 3, 100, 101, 102, 19, 115, 101, 110, + 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, + 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, + 112, 112, 4, 108, 111, 103, 111, 4, 108, 111, 103, 111, 12, 255, 0, 252, 3, 0, 0, 253, + 1, 16, 0, 0, 0, 81, 0, 0, 5, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, + 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, + 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 115, 104, + 111, 114, 116, 95, 110, 97, 109, 101, 10, 115, 104, 111, 114, 116, 95, 110, 97, 109, + 101, 12, 255, 0, 0, 1, 0, 0, 253, 8, 64, 0, 0, 0, 91, 0, 0, 6, 3, 100, 101, 102, 19, + 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, + 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, + 114, 95, 97, 112, 112, 15, 115, 121, 115, 95, 112, 114, 111, 100, 117, 99, 101, 114, + 95, 105, 100, 15, 115, 121, 115, 95, 112, 114, 111, 100, 117, 99, 101, 114, 95, 105, + 100, 12, 63, 0, 20, 0, 0, 0, 8, 8, 64, 0, 0, 0, 71, 0, 0, 7, 3, 100, 101, 102, 19, 115, + 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, + 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, + 97, 112, 112, 5, 97, 114, 101, 97, 115, 5, 97, 114, 101, 97, 115, 12, 255, 0, 0, 16, 0, + 0, 253, 0, 0, 0, 0, 0, 75, 0, 0, 8, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, + 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, + 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 7, 116, + 97, 114, 103, 101, 116, 115, 7, 116, 97, 114, 103, 101, 116, 115, 12, 255, 0, 252, 3, + 0, 0, 253, 0, 0, 0, 0, 0, 77, 0, 0, 9, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, + 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, + 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 8, + 110, 116, 100, 95, 105, 109, 103, 115, 8, 110, 116, 100, 95, 105, 109, 103, 115, 12, + 255, 0, 252, 255, 3, 0, 252, 16, 0, 0, 0, 0, 85, 0, 0, 10, 3, 100, 101, 102, 19, 115, + 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, + 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, + 97, 112, 112, 12, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 115, 12, 97, 112, + 112, 108, 105, 99, 97, 116, 105, 111, 110, 115, 12, 255, 0, 252, 255, 3, 0, 252, 16, 0, + 0, 0, 0, 85, 0, 0, 11, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, + 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, + 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 12, 115, 121, 115, + 95, 99, 108, 111, 117, 100, 95, 105, 100, 12, 115, 121, 115, 95, 99, 108, 111, 117, + 100, 95, 105, 100, 12, 63, 0, 20, 0, 0, 0, 8, 1, 16, 0, 0, 0, 83, 0, 0, 12, 3, 100, + 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, + 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, + 112, 104, 101, 114, 95, 97, 112, 112, 11, 116, 101, 110, 97, 110, 116, 95, 110, 97, + 109, 101, 11, 116, 101, 110, 97, 110, 116, 95, 110, 97, 109, 101, 12, 255, 0, 252, 3, + 0, 0, 253, 1, 16, 0, 0, 0, 65, 0, 0, 13, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, + 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, + 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 2, + 105, 112, 2, 105, 112, 12, 255, 0, 80, 0, 0, 0, 253, 0, 0, 0, 0, 0, 69, 0, 0, 14, 3, + 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, + 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, + 112, 104, 101, 114, 95, 97, 112, 112, 4, 112, 111, 114, 116, 4, 112, 111, 114, 116, 12, + 63, 0, 11, 0, 0, 0, 3, 0, 0, 0, 0, 0, 83, 0, 0, 15, 3, 100, 101, 102, 19, 115, 101, + 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, + 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, + 112, 112, 11, 100, 111, 109, 97, 105, 110, 95, 110, 97, 109, 101, 11, 100, 111, 109, + 97, 105, 110, 95, 110, 97, 109, 101, 12, 255, 0, 0, 8, 0, 0, 253, 0, 0, 0, 0, 0, 79, 0, + 0, 16, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, + 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, + 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 9, 108, 111, 103, 105, 110, 95, 117, + 114, 108, 9, 108, 111, 103, 105, 110, 95, 117, 114, 108, 12, 255, 0, 0, 8, 0, 0, 253, + 0, 0, 0, 0, 0, 73, 0, 0, 17, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, + 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, + 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 6, 105, 115, + 95, 115, 115, 108, 6, 105, 115, 95, 115, 115, 108, 12, 63, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 87, 0, 0, 18, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, + 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, + 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 13, 105, 110, 116, + 114, 97, 110, 101, 116, 95, 105, 110, 102, 111, 13, 105, 110, 116, 114, 97, 110, 101, + 116, 95, 105, 110, 102, 111, 12, 255, 0, 252, 255, 3, 0, 252, 16, 0, 0, 0, 0, 81, 0, 0, + 19, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, + 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, + 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 114, 101, 97, 116, 101, 100, 95, + 97, 116, 10, 99, 114, 101, 97, 116, 101, 100, 95, 97, 116, 12, 63, 0, 23, 0, 0, 0, 12, + 128, 0, 3, 0, 0, 81, 0, 0, 20, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, + 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, + 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 117, 112, + 100, 97, 116, 101, 100, 95, 97, 116, 10, 117, 112, 100, 97, 116, 101, 100, 95, 97, 116, + 12, 63, 0, 23, 0, 0, 0, 12, 128, 0, 3, 0, 0, 81, 0, 0, 21, 3, 100, 101, 102, 19, 115, + 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, + 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, + 97, 112, 112, 10, 100, 101, 108, 101, 116, 101, 100, 95, 97, 116, 10, 100, 101, 108, + 101, 116, 101, 100, 95, 97, 116, 12, 63, 0, 23, 0, 0, 0, 12, 128, 0, 3, 0, 0, 75, 0, 0, + 22, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, + 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, + 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 7, 97, 112, 112, 114, 111, 118, 101, 7, + 97, 112, 112, 114, 111, 118, 101, 12, 63, 0, 10, 0, 0, 0, 3, 33, 0, 0, 0, 0, 75, 0, 0, + 23, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, + 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, + 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 7, 117, 115, 101, 114, 95, 105, 100, 7, + 117, 115, 101, 114, 95, 105, 100, 12, 63, 0, 20, 0, 0, 0, 8, 33, 16, 0, 0, 0, 69, 0, 0, + 24, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, + 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, + 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 4, 117, 117, 105, 100, 4, 117, 117, 105, + 100, 12, 255, 0, 252, 3, 0, 0, 253, 9, 80, 0, 0, 0, 75, 0, 0, 25, 3, 100, 101, 102, 19, + 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, + 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, + 114, 95, 97, 112, 112, 7, 112, 114, 111, 99, 101, 115, 115, 7, 112, 114, 111, 99, 101, + 115, 115, 12, 255, 0, 252, 3, 0, 0, 253, 0, 0, 0, 0, 0, 73, 0, 0, 26, 3, 100, 101, 102, + 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, + 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, + 101, 114, 95, 97, 112, 112, 6, 114, 101, 115, 117, 108, 116, 6, 114, 101, 115, 117, + 108, 116, 12, 63, 0, 10, 0, 0, 0, 3, 32, 0, 0, 0, 0, 75, 0, 0, 27, 3, 100, 101, 102, + 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, + 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, + 101, 114, 95, 97, 112, 112, 7, 111, 115, 95, 110, 97, 109, 101, 7, 111, 115, 95, 110, + 97, 109, 101, 12, 255, 0, 252, 3, 0, 0, 253, 0, 0, 0, 0, 0, 81, 0, 0, 28, 3, 100, 101, + 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, + 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, + 104, 101, 114, 95, 97, 112, 112, 10, 111, 115, 95, 118, 101, 114, 115, 105, 111, 110, + 10, 111, 115, 95, 118, 101, 114, 115, 105, 111, 110, 12, 255, 0, 252, 3, 0, 0, 253, 0, + 0, 0, 0, 0, 73, 0, 0, 29, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, + 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, + 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 6, 107, 101, 114, + 110, 101, 108, 6, 107, 101, 114, 110, 101, 108, 12, 255, 0, 252, 3, 0, 0, 253, 0, 0, 0, + 0, 0, 77, 0, 0, 30, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, 110, 101, 108, 95, + 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, 104, 101, 114, 95, + 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 8, 112, 114, 111, 116, + 111, 99, 111, 108, 8, 112, 114, 111, 116, 111, 99, 111, 108, 12, 63, 0, 255, 255, 255, + 255, 245, 144, 0, 0, 0, 0, 69, 0, 0, 31, 3, 100, 101, 102, 19, 115, 101, 110, 116, 105, + 110, 101, 108, 95, 102, 108, 111, 119, 95, 97, 100, 109, 105, 110, 10, 99, 105, 112, + 104, 101, 114, 95, 97, 112, 112, 10, 99, 105, 112, 104, 101, 114, 95, 97, 112, 112, 4, + 109, 97, 114, 107, 4, 109, 97, 114, 107, 12, 63, 0, 20, 0, 0, 0, 8, 1, 0, 0, 0, 0, 5, + 0, 0, 32, 254, 0, 0, 2, 0, 224, 0, 0, 33, 0, 0, 0, 34, 0, 1, 0, 0, 0, 0, 0, 0, 0, 24, + 233, 163, 142, 233, 153, 169, 230, 188, 143, 230, 180, 158, 230, 131, 133, 230, 138, + 165, 229, 185, 179, 229, 143, 176, 4, 83, 65, 86, 83, 12, 233, 163, 142, 233, 153, 169, + 229, 185, 179, 229, 143, 176, 9, 0, 0, 0, 0, 0, 0, 0, 4, 91, 51, 52, 93, 3, 91, 51, 93, + 2, 91, 93, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, + 48, 46, 50, 49, 53, 80, 0, 0, 0, 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 7, 2, 11, 11, + 0, 232, 253, 0, 0, 11, 232, 7, 8, 26, 15, 27, 59, 112, 225, 8, 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 36, 48, 49, 57, 49, 56, 100, 57, 53, 45, 97, 50, 53, 56, 45, 55, 100, + 98, 52, 45, 98, 52, 50, 101, 45, 101, 51, 52, 98, 98, 99, 57, 49, 97, 53, 102, 52, 0, + 1, 0, 0, 0, 6, 67, 101, 110, 116, 111, 115, 3, 55, 46, 54, 4, 53, 46, 49, 53, 8, 91, + 34, 72, 84, 84, 80, 34, 93, 1, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 34, 0, 0, 0, 34, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 14, 79, 65, 231, 174, 161, 231, 144, 134, 231, 179, 187, 231, 187, + 159, 5, 71, 69, 79, 65, 32, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4, 91, 51, 53, 93, 3, 91, 49, + 93, 2, 91, 93, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, + 48, 48, 46, 49, 51, 53, 138, 19, 0, 0, 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 8, 2, + 11, 12, 23, 136, 66, 13, 0, 11, 232, 7, 8, 10, 14, 37, 55, 232, 50, 13, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 36, 48, 49, 57, 49, 51, 98, 48, 50, 45, 48, 100, 49, 53, 45, + 55, 99, 101, 50, 45, 57, 54, 101, 57, 45, 99, 100, 56, 101, 57, 51, 55, 57, 101, 97, + 50, 98, 0, 1, 0, 0, 0, 5, 113, 105, 108, 105, 110, 3, 86, 49, 48, 0, 11, 91, 34, 84, + 76, 83, 124, 49, 46, 50, 34, 93, 2, 0, 0, 0, 0, 0, 0, 0, 13, 1, 0, 35, 0, 0, 0, 34, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 18, 232, 175, 129, 230, 141, 174, 231, 174, 161, 231, 144, 134, + 231, 179, 187, 231, 187, 159, 9, 90, 74, 71, 76, 45, 52, 48, 48, 48, 0, 20, 0, 0, 0, 0, + 0, 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, 48, 46, 49, 48, 52, 187, 1, 0, 0, + 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 6, 19, 11, 13, 27, 128, 175, 2, 0, 11, 232, 7, + 8, 20, 10, 15, 25, 208, 102, 5, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 36, 48, 49, 57, + 49, 54, 100, 57, 49, 45, 52, 102, 97, 52, 45, 55, 49, 102, 55, 45, 56, 50, 52, 99, 45, + 97, 97, 51, 56, 49, 54, 54, 48, 52, 101, 51, 98, 0, 1, 0, 0, 0, 5, 75, 121, 108, 105, + 110, 7, 86, 49, 48, 45, 83, 80, 49, 0, 66, 91, 34, 84, 76, 83, 124, 49, 46, 48, 34, 44, + 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 50, 34, + 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, 34, 84, 76, 83, 124, 103, 109, 45, + 49, 46, 48, 34, 44, 32, 34, 72, 84, 84, 80, 34, 93, 3, 0, 0, 0, 0, 0, 0, 0, 26, 1, 0, + 36, 0, 0, 0, 34, 0, 4, 0, 0, 0, 0, 0, 0, 0, 18, 230, 150, 135, 228, 187, 182, 229, 173, + 152, 229, 130, 168, 231, 179, 187, 231, 187, 159, 4, 70, 87, 83, 68, 18, 230, 150, 135, + 228, 187, 182, 229, 173, 152, 229, 130, 168, 231, 179, 187, 231, 187, 159, 46, 0, 0, 0, + 0, 0, 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, 48, 46, 49, 48, 53, 187, 1, 0, + 0, 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 8, 19, 11, 15, 41, 80, 171, 4, 0, 11, 232, + 7, 8, 20, 10, 13, 45, 8, 153, 9, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 36, 48, 49, 57, + 49, 54, 100, 56, 102, 45, 99, 97, 49, 55, 45, 55, 49, 102, 55, 45, 57, 56, 48, 101, 45, + 100, 51, 50, 99, 52, 54, 54, 102, 53, 98, 100, 49, 0, 1, 0, 0, 0, 5, 75, 121, 108, 105, + 110, 7, 86, 49, 48, 45, 83, 80, 49, 0, 66, 91, 34, 84, 76, 83, 124, 49, 46, 48, 34, 44, + 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 50, 34, + 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, 34, 84, 76, 83, 124, 103, 109, 45, + 49, 46, 48, 34, 44, 32, 34, 72, 84, 84, 80, 34, 93, 4, 0, 0, 0, 0, 0, 0, 0, 29, 1, 0, + 37, 0, 0, 0, 34, 0, 5, 0, 0, 0, 0, 0, 0, 0, 18, 231, 148, 181, 229, 173, 144, 230, 138, + 165, 232, 161, 168, 231, 179, 187, 231, 187, 159, 7, 69, 84, 83, 49, 48, 50, 48, 18, + 231, 148, 181, 229, 173, 144, 230, 138, 165, 232, 161, 168, 231, 179, 187, 231, 187, + 159, 48, 0, 0, 0, 0, 0, 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, 48, 46, 50, + 51, 51, 187, 1, 0, 0, 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 5, 19, 11, 17, 17, 8, + 183, 4, 0, 11, 232, 7, 8, 20, 10, 13, 7, 160, 15, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 36, 48, 49, 57, 49, 54, 100, 56, 102, 45, 51, 51, 51, 54, 45, 55, 49, 102, 55, + 45, 98, 55, 99, 100, 45, 57, 99, 54, 102, 98, 56, 54, 48, 48, 48, 102, 100, 0, 1, 0, 0, + 0, 5, 75, 121, 108, 105, 110, 7, 86, 49, 48, 45, 83, 80, 49, 0, 66, 91, 34, 84, 76, 83, + 124, 49, 46, 48, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, 84, 76, + 83, 124, 49, 46, 50, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, 34, 84, + 76, 83, 124, 103, 109, 45, 49, 46, 48, 34, 44, 32, 34, 72, 84, 84, 80, 34, 93, 5, 0, 0, + 0, 0, 0, 0, 0, 33, 1, 0, 38, 0, 0, 0, 34, 0, 6, 0, 0, 0, 0, 0, 0, 0, 15, 71, 73, 83, + 232, 129, 148, 233, 152, 178, 231, 179, 187, 231, 187, 159, 8, 71, 73, 83, 83, 71, 73, + 83, 83, 15, 71, 73, 83, 232, 129, 148, 233, 152, 178, 231, 179, 187, 231, 187, 159, 40, + 0, 0, 0, 0, 0, 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 50, 48, 48, 46, 50, 49, 51, + 187, 1, 0, 0, 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 8, 19, 11, 19, 23, 112, 148, 0, + 0, 11, 232, 7, 8, 20, 10, 12, 27, 0, 83, 7, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 36, + 48, 49, 57, 49, 54, 100, 56, 101, 45, 57, 56, 100, 50, 45, 55, 49, 102, 55, 45, 98, 53, + 100, 97, 45, 51, 98, 97, 55, 57, 49, 51, 97, 100, 98, 55, 55, 0, 1, 0, 0, 0, 5, 75, + 121, 108, 105, 110, 7, 86, 49, 48, 45, 83, 80, 49, 0, 75, 91, 34, 84, 76, 83, 124, 49, + 46, 48, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, 84, 76, 83, 124, + 49, 46, 50, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, 34, 84, 76, 83, + 124, 103, 109, 45, 49, 46, 48, 34, 44, 32, 34, 73, 80, 83, 101, 99, 34, 44, 32, 34, 72, + 84, 84, 80, 34, 93, 6, 0, 0, 0, 0, 0, 0, 0, 10, 1, 0, 39, 0, 0, 0, 34, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 12, 231, 165, 158, 231, 155, 190, 233, 152, 178, 230, 138, 164, 6, 78, 83, + 71, 51, 56, 48, 12, 78, 83, 71, 51, 56, 48, 48, 45, 84, 71, 50, 53, 33, 0, 0, 0, 0, 0, + 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, 48, 46, 50, 49, 53, 187, 1, 0, 0, 0, + 0, 4, 110, 117, 108, 108, 11, 231, 7, 8, 19, 11, 21, 54, 64, 72, 14, 0, 11, 232, 7, 8, + 20, 10, 14, 47, 88, 229, 8, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 36, 48, 49, 57, 49, + 54, 100, 57, 48, 45, 98, 99, 49, 57, 45, 55, 49, 102, 55, 45, 56, 99, 54, 51, 45, 99, + 55, 52, 101, 53, 49, 100, 102, 51, 100, 98, 52, 0, 1, 0, 0, 0, 7, 117, 110, 107, 110, + 111, 119, 110, 7, 117, 110, 107, 110, 111, 119, 110, 0, 58, 91, 34, 84, 76, 83, 124, + 49, 46, 48, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, 84, 76, 83, + 124, 49, 46, 50, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, 34, 84, 76, + 83, 124, 103, 109, 45, 49, 46, 48, 34, 93, 7, 0, 0, 0, 0, 0, 0, 0, 53, 1, 0, 40, 0, 0, + 0, 34, 0, 8, 0, 0, 0, 0, 0, 0, 0, 24, 230, 153, 186, 230, 133, 167, 232, 176, 131, 229, + 186, 166, 230, 142, 167, 229, 136, 182, 231, 179, 187, 231, 187, 159, 8, 73, 68, 67, + 83, 45, 50, 48, 48, 24, 230, 153, 186, 230, 133, 167, 232, 176, 131, 229, 186, 166, + 230, 142, 167, 229, 136, 182, 231, 179, 187, 231, 187, 159, 49, 0, 0, 0, 0, 0, 0, 0, 4, + 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 15, + 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, 48, 46, 50, 50, 49, 187, 1, 0, 0, 0, 0, 4, 110, + 117, 108, 108, 11, 232, 7, 8, 19, 11, 24, 18, 48, 27, 15, 0, 11, 232, 7, 8, 20, 10, 11, + 30, 88, 92, 10, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 36, 48, 49, 57, 49, 54, 100, 56, + 100, 45, 98, 97, 102, 48, 45, 55, 49, 102, 55, 45, 97, 98, 52, 100, 45, 54, 101, 97, + 48, 100, 51, 50, 102, 55, 48, 102, 56, 0, 1, 0, 0, 0, 7, 117, 110, 107, 110, 111, 119, + 110, 7, 117, 110, 107, 110, 111, 119, 110, 0, 75, 91, 34, 84, 76, 83, 124, 49, 46, 48, + 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, + 50, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, 34, 84, 76, 83, 124, 103, + 109, 45, 49, 46, 48, 34, 44, 32, 34, 73, 80, 83, 101, 99, 34, 44, 32, 34, 72, 84, 84, + 80, 34, 93, 8, 0, 0, 0, 0, 0, 0, 0, 35, 1, 0, 41, 0, 0, 0, 34, 0, 9, 0, 0, 0, 0, 0, 0, + 0, 24, 231, 187, 136, 231, 171, 175, 229, 174, 137, 229, 133, 168, 231, 174, 161, 231, + 144, 134, 231, 179, 187, 231, 187, 159, 8, 77, 83, 77, 83, 45, 51, 57, 48, 6, 229, 164, + 169, 230, 147, 142, 47, 0, 0, 0, 0, 0, 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, + 108, 2, 91, 93, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, + 48, 48, 46, 50, 50, 50, 187, 1, 0, 0, 0, 0, 4, 110, 117, 108, 108, 11, 232, 7, 8, 19, + 11, 27, 30, 88, 122, 5, 0, 11, 232, 7, 8, 20, 15, 37, 20, 32, 179, 4, 0, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 36, 48, 49, 57, 49, 54, 101, 98, 56, 45, 48, 56, 97, 101, 45, 55, + 52, 99, 99, 45, 56, 98, 54, 56, 45, 49, 52, 52, 100, 54, 51, 48, 99, 53, 56, 98, 50, 0, + 1, 0, 0, 0, 7, 117, 110, 107, 110, 111, 119, 110, 7, 117, 110, 107, 110, 111, 119, 110, + 0, 75, 91, 34, 84, 76, 83, 124, 49, 46, 48, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, + 49, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 50, 34, 44, 32, 34, 84, 76, 83, 124, 49, + 46, 51, 34, 44, 32, 34, 73, 80, 83, 101, 99, 34, 44, 32, 34, 84, 76, 83, 124, 103, 109, + 45, 49, 46, 48, 34, 44, 32, 34, 72, 84, 84, 80, 34, 93, 9, 0, 0, 0, 0, 0, 0, 0, 54, 1, + 0, 42, 0, 0, 0, 34, 0, 10, 0, 0, 0, 0, 0, 0, 0, 24, 230, 153, 186, 230, 133, 167, 232, + 129, 148, 229, 138, 168, 232, 129, 148, 229, 139, 164, 231, 179, 187, 231, 187, 159, 9, + 73, 76, 76, 83, 45, 49, 50, 48, 48, 24, 230, 153, 186, 230, 133, 167, 232, 129, 148, + 229, 138, 168, 232, 129, 148, 229, 139, 164, 231, 179, 187, 231, 187, 159, 46, 0, 0, 0, + 0, 0, 0, 0, 4, 110, 117, 108, 108, 4, 110, 117, 108, 108, 2, 91, 93, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 15, 49, 57, 50, 46, 49, 54, 56, 46, 49, 48, 48, 46, 49, 51, 51, 187, 1, 0, + 0, 0, 0, 4, 110, 117, 108, 108, 11, 231, 7, 9, 19, 11, 29, 46, 224, 105, 11, 0, 11, + 232, 7, 8, 20, 10, 6, 40, 208, 179, 13, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 36, 48, + 49, 57, 49, 54, 100, 56, 57, 45, 52, 101, 102, 99, 45, 55, 49, 102, 55, 45, 56, 97, + 102, 49, 45, 54, 54, 57, 49, 97, 98, 99, 48, 102, 101, 52, 101, 0, 1, 0, 0, 0, 7, 117, + 110, 107, 110, 111, 119, 110, 7, 117, 110, 107, 110, 111, 119, 110, 0, 75, 91, 34, 84, + 76, 83, 124, 49, 46, 48, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 49, 34, 44, 32, 34, + 84, 76, 83, 124, 49, 46, 50, 34, 44, 32, 34, 84, 76, 83, 124, 49, 46, 51, 34, 44, 32, + 34, 84, 76, 83, 124, 103, 109, 45, 49, 46, 48, 34, 44, 32, 34, 73, 80, 83, 101, 99, 34, + 44, 32, 34, 72, 84, 84, 80, 34, 93, 10, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 43, 254, 0, 0, 2, + 0, + ]; + + let (rem, _) = parse_stmt_execute_response(pkt).unwrap(); + assert!(rem.is_empty()); + + let pkt: &[u8] = &[ + 0x01, 0x00, 0x00, 0x01, 0x20, 0x47, 0x00, 0x00, 0x02, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64, 0x0c, 0x3f, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x23, 0x42, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x03, + 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x73, 0x79, 0x73, + 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x69, 0x64, 0x0d, 0x73, 0x79, 0x73, + 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x08, 0x01, 0x10, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x04, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0c, 0x73, 0x79, 0x73, 0x5f, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x64, 0x0c, 0x73, 0x79, 0x73, 0x5f, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x10, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x05, 0x03, 0x64, 0x65, 0x66, + 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x03, 0x69, 0x70, 0x73, 0x03, 0x69, 0x70, 0x73, + 0x0c, 0xff, 0x00, 0xfc, 0xff, 0x03, 0x00, 0xfc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x51, + 0x00, 0x00, 0x06, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, + 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x07, + 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03, 0x01, 0x10, 0x00, 0x00, 0x00, + 0x67, 0x00, 0x00, 0x07, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, + 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, + 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x0c, 0xff, 0x00, 0xfc, + 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x08, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x16, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, + 0x5f, 0x73, 0x74, 0x72, 0x16, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x5f, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x0c, + 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x00, + 0x00, 0x09, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x14, 0x61, + 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x61, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x14, 0x61, 0x73, 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x0c, 0xff, + 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, + 0x0a, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x61, 0x73, + 0x79, 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, + 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x61, 0x73, 0x79, 0x6d, 0x6d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, + 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x0c, 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, 0xfd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x0b, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, + 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x67, + 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x0c, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x67, + 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x12, 0x68, 0x61, 0x73, + 0x68, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x73, 0x74, + 0x72, 0x0c, 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6b, 0x00, 0x00, 0x0d, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, + 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x14, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x14, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, + 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, + 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, + 0x00, 0x00, 0x0e, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, + 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, + 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x67, + 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, + 0x74, 0x68, 0x6d, 0x5f, 0x73, 0x74, 0x72, 0x0c, 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x0f, 0x03, 0x64, 0x65, 0x66, + 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x61, + 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x0f, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x0c, 0xff, 0x00, 0xfc, + 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x10, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x69, 0x73, 0x5f, 0x6b, + 0x65, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x0d, 0x69, 0x73, 0x5f, 0x6b, + 0x65, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x0c, 0x3f, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x11, 0x03, 0x64, + 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x0c, 0xff, 0x00, 0xfc, 0xff, 0x03, 0x00, 0xfc, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x12, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x06, 0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x06, 0x72, + 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x0c, 0xff, 0x00, 0xfc, 0xff, 0x03, 0x00, 0xfc, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x13, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, + 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0c, 0x3f, + 0x00, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x03, 0x00, 0x00, 0x57, 0x00, 0x00, + 0x14, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0a, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0c, 0x3f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x80, + 0x00, 0x03, 0x00, 0x00, 0x57, 0x00, 0x00, 0x15, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, + 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x0c, 0x3f, + 0x00, 0x17, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x03, 0x00, 0x00, 0x61, 0x00, 0x00, + 0x16, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, + 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0f, 0x67, 0x6d, + 0x74, 0x5f, 0x30, 0x30, 0x35, 0x33, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x0f, + 0x67, 0x6d, 0x74, 0x5f, 0x30, 0x30, 0x35, 0x33, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x0c, 0xff, 0x00, 0xfc, 0xff, 0x03, 0x00, 0xfc, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x00, 0x17, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, + 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x0b, 0x67, 0x6d, 0x74, 0x5f, 0x30, 0x30, 0x35, 0x33, 0x5f, 0x69, 0x70, 0x0b, 0x67, + 0x6d, 0x74, 0x5f, 0x30, 0x30, 0x35, 0x33, 0x5f, 0x69, 0x70, 0x0c, 0xff, 0x00, 0xfc, + 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x18, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x67, 0x6d, 0x74, 0x5f, + 0x30, 0x30, 0x35, 0x33, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x0d, 0x67, 0x6d, 0x74, 0x5f, + 0x30, 0x30, 0x35, 0x33, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x0c, 0x3f, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x19, 0x03, 0x64, + 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x07, 0x61, 0x70, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x07, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x0c, 0x3f, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x1a, 0x03, + 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x07, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x0c, 0x3f, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x21, 0x10, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x1b, + 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x04, 0x75, 0x75, 0x69, + 0x64, 0x04, 0x75, 0x75, 0x69, 0x64, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, + 0x09, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x1c, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x07, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x1d, 0x03, 0x64, 0x65, 0x66, + 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x0c, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x1e, 0x03, 0x64, 0x65, 0x66, 0x13, + 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x07, 0x6f, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x07, + 0x6f, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x1f, 0x03, 0x64, 0x65, 0x66, + 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x0c, 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, + 0x00, 0x00, 0x20, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, + 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x06, + 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x0c, + 0xff, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, + 0x00, 0x21, 0x03, 0x64, 0x65, 0x66, 0x13, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0d, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x0d, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x04, 0x6d, + 0x61, 0x72, 0x6b, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x0c, 0x3f, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x22, 0xfe, 0x00, 0x00, + 0x22, 0x00, 0x05, 0x00, 0x00, 0x23, 0xfe, 0x00, 0x00, 0x22, 0x00, + ]; + + let (rem, _) = parse_stmt_execute_response(pkt).unwrap(); + assert!(rem.is_empty()); + } + + #[test] + fn test_parse_close_stmt_request() { + let pkt: &[u8] = &[0x05, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x00]; + + let (rem, request) = parse_request(pkt, Some(1), None, None, 0).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(request.command_code, 0x19); + + let command = request.command; + + if let MysqlCommand::StmtClose { statement_id } = command { + assert_eq!(statement_id, 1); + } else { + unreachable!(); + } + } + + #[test] + fn test_parse_ping_request() { + let pkt: &[u8] = &[0x01, 0x00, 0x00, 0x00, 0x0e]; + let (rem, request) = parse_request(pkt, None, None, None, 0).unwrap(); + + assert!(rem.is_empty()); + assert_eq!(request.command, MysqlCommand::Ping); + } + + #[test] + fn test_parse_unknown_request() {} +} diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index e82d8688f55..2f717f034ae 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -69,6 +69,7 @@ const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { { ALPROTO_BITTORRENT_DHT, "bittorrent-dht" }, { ALPROTO_POP3, "pop3" }, { ALPROTO_HTTP, "http" }, + { ALPROTO_MYSQL, "mysql" }, { ALPROTO_FAILED, "failed" }, #ifdef UNITTESTS { ALPROTO_TEST, "test" }, diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 348203ac2f5..0ba973a2edb 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -64,6 +64,7 @@ enum AppProtoEnum { ALPROTO_HTTP2, ALPROTO_BITTORRENT_DHT, ALPROTO_POP3, + ALPROTO_MYSQL, // signature-only (ie not seen in flow) // HTTP for any version (ALPROTO_HTTP1 (version 1) or ALPROTO_HTTP2) diff --git a/suricata.yaml.in b/suricata.yaml.in index 2e5f722c366..78f0fc4bbb8 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -916,6 +916,12 @@ app-layer: stream-depth: 0 # Maximum number of live PostgreSQL transactions per flow # max-tx: 1024 + mysql: + enabled: no + # Stream reassembly size for MySQL. By default, track it completely. + stream-depth: 0 + # Maximum number of live MySQL transactions per flow + # max-tx: 1024 dcerpc: enabled: yes # Maximum number of live DCERPC transactions per flow