From b975a726b474949939d4ad7cc4a3c1eca4119029 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Sat, 24 Feb 2024 12:07:02 +0300 Subject: [PATCH 1/7] Bump dependencies --- Cargo.toml | 12 ++++++------ src/conn/mod.rs | 5 ++++- src/error/mod.rs | 5 ++++- src/lib.rs | 4 +--- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e409e126..23b55fc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ keyed_priority_queue = "0.4" lazy_static = "1" lru = "0.12.0" mio = { version = "0.8.0", features = ["os-poll", "net"] } -mysql_common = { version = "0.31", default-features = false } +mysql_common = { version = "0.32", default-features = false } once_cell = "1.7.2" pem = "3.0" percent-encoding = "2.1.0" @@ -42,7 +42,7 @@ twox-hash = "1" url = "2.1" [dependencies.tokio-rustls] -version = "0.24.0" +version = "0.25" optional = true [dependencies.tokio-native-tls] @@ -54,12 +54,12 @@ version = "0.2" optional = true [dependencies.rustls] -version = "0.21.0" -features = ["dangerous_configuration"] +version = "0.22.2" +features = [] optional = true [dependencies.rustls-pemfile] -version = "1.0.1" +version = "2.1.0" optional = true [dependencies.webpki] @@ -68,7 +68,7 @@ features = ["std"] optional = true [dependencies.webpki-roots] -version = "0.25.0" +version = "0.26.1" optional = true [dev-dependencies] diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 00de30f6..8d58f72b 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -7,7 +7,6 @@ // modified, or distributed except according to those terms. use futures_util::FutureExt; -pub use mysql_common::named_params; use mysql_common::{ constants::{DEFAULT_MAX_ALLOWED_PACKET, UTF8MB4_GENERAL_CI, UTF8_GENERAL_CI}, @@ -575,6 +574,10 @@ impl Conn { Some(self.inner.auth_plugin.borrow()), self.capabilities(), Default::default(), // TODO: Add support + self.inner + .opts + .max_allowed_packet() + .unwrap_or(DEFAULT_MAX_ALLOWED_PACKET) as u32, ); // Serialize here to satisfy borrow checker. diff --git a/src/error/mod.rs b/src/error/mod.rs index e14a2578..983f274e 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -243,7 +243,10 @@ impl From> for ServerError { ServerError { code: packet.error_code(), message: packet.message_str().into(), - state: packet.sql_state_str().into(), + state: packet + .sql_state_ref() + .map(|s| s.as_str().into_owned()) + .unwrap_or_else(|| "HY000".to_owned()), } } } diff --git a/src/lib.rs b/src/lib.rs index af44f67f..f0605910 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -610,10 +610,8 @@ pub mod test_misc { #[allow(dead_code)] #[allow(unreachable_code)] - fn error_should_implement_send_and_sync() { + fn error_should_implement_send_and_sync(err: crate::Error) { fn _dummy(_: T) {} - #[allow(unused_variables)] - let err: crate::Error = panic!(); _dummy(err); } From 0540dc118a69f567c69133023117d7946589cc5a Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Mon, 18 Mar 2024 23:35:28 +0300 Subject: [PATCH 2/7] Fix ci for MySql 5.6 --- azure-pipelines.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 359eb6cb..c3954334 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -94,9 +94,9 @@ jobs: maxParallel: 10 matrix: v80: - DB_VERSION: "8-debian" + DB_VERSION: "8.0-debian" v57: - DB_VERSION: "5-debian" + DB_VERSION: "5.7-debian" v56: DB_VERSION: "5.6" steps: @@ -114,9 +114,13 @@ jobs: displayName: Run MySql in Docker - bash: | docker exec container bash -l -c "mysql -uroot -ppassword -e \"SET old_passwords = 1; GRANT ALL PRIVILEGES ON *.* TO 'root2'@'%' IDENTIFIED WITH mysql_old_password AS 'password'; SET PASSWORD FOR 'root2'@'%' = OLD_PASSWORD('password')\""; + docker exec container bash -l -c "echo 'deb [trusted=yes] http://archive.debian.org/debian/ stretch main non-free contrib' > /etc/apt/sources.list" + docker exec container bash -l -c "echo 'deb-src [trusted=yes] http://archive.debian.org/debian/ stretch main non-free contrib ' >> /etc/apt/sources.list" + docker exec container bash -l -c "echo 'deb [trusted=yes] http://archive.debian.org/debian-security/ stretch/updates main non-free contrib' >> /etc/apt/sources.list" + docker exec container bash -l -c "echo 'deb [trusted=yes] http://repo.mysql.com/apt/debian/ stretch mysql-5.6' > /etc/apt/sources.list.d/mysql.list" condition: eq(variables['DB_VERSION'], '5.6') - bash: | - docker exec container bash -l -c "apt-get update" + docker exec container bash -l -c "apt-get --allow-unauthenticated -y update" docker exec container bash -l -c "apt-get install -y curl clang libssl-dev pkg-config build-essential" docker exec container bash -l -c "curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable" displayName: Install Rust in docker From d71a75a0b16473db2a600c299481413a802d2d60 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Mon, 18 Mar 2024 23:36:25 +0300 Subject: [PATCH 3/7] ci: reduce matrix for TestMariaDb --- azure-pipelines.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3954334..387b5c4b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -142,20 +142,16 @@ jobs: strategy: maxParallel: 10 matrix: - v107: - DB_VERSION: "10.7" + v113: + DB_VERSION: "11.3" + v1011: + DB_VERSION: "10.11" v106: DB_VERSION: "10.6" v105: DB_VERSION: "10.5" v104: DB_VERSION: "10.4" - v103: - DB_VERSION: "10.3" - v102: - DB_VERSION: "10.2" - v101: - DB_VERSION: "10.1" steps: - bash: | sudo apt-get update From 414266537ba329d7853879e152a3e4a65a9b15ab Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Mon, 18 Mar 2024 23:39:16 +0300 Subject: [PATCH 4/7] Proxy mysql_common features --- Cargo.toml | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23b55fc4..695bb843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,26 +80,35 @@ tokio = { version = "1.0", features = ["macros", "rt", "rt-multi-thread"] } [features] default = [ "flate2/zlib", - "mysql_common/bigdecimal", - "mysql_common/rust_decimal", - "mysql_common/time", - "mysql_common/frunk", + "bigdecimal", + "rust_decimal", + "time", + "frunk", "derive", "native-tls-tls", "binlog", ] + default-rustls = [ "flate2/rust_backend", - "mysql_common/bigdecimal", - "mysql_common/rust_decimal", - "mysql_common/time", - "mysql_common/frunk", + "bigdecimal", + "rust_decimal", + "time", + "frunk", "derive", "rustls-tls", "binlog", ] + +# minimal feature set with system flate2 impl minimal = ["flate2/zlib"] +# minimal feature set with rust flate2 impl +minimal-rust = ["flate2/rust_backend"] + +# native-tls based TLS support native-tls-tls = ["native-tls", "tokio-native-tls"] + +# rustls based TLS support rustls-tls = [ "rustls", "tokio-rustls", @@ -107,11 +116,20 @@ rustls-tls = [ "webpki-roots", "rustls-pemfile", ] -tracing = ["dep:tracing"] + +# mysql_common features derive = ["mysql_common/derive"] -nightly = [] +chrono = ["mysql_common/chrono"] +time = ["mysql_common/time"] +bigdecimal = ["mysql_common/bigdecimal"] +rust_decimal = ["mysql_common/rust_decimal"] +frunk = ["mysql_common/frunk"] binlog = ["mysql_common/binlog"] +# other features +tracing = ["dep:tracing"] +nightly = [] + [lib] name = "mysql_async" path = "src/lib.rs" From 174f40f672ad1dfc54fcb25e439e46ea2bbeed88 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Mon, 18 Mar 2024 23:39:59 +0300 Subject: [PATCH 5/7] Bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 695bb843..2e0f56de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT/Apache-2.0" name = "mysql_async" readme = "README.md" repository = "https://github.com/blackbeam/mysql_async" -version = "0.33.0" +version = "0.34.0" exclude = ["test/*"] edition = "2018" categories = ["asynchronous", "database"] From c5aa660e5174157be89b397a340a4677159ddc03 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 19 Mar 2024 00:13:15 +0300 Subject: [PATCH 6/7] Fix rustls feature --- Cargo.toml | 2 +- src/error/mod.rs | 2 +- src/error/tls/rustls_error.rs | 11 ++++ src/io/tls/rustls_io.rs | 120 ++++++++++++++++++++++++---------- src/lib.rs | 3 +- src/opts/rustls_opts.rs | 26 ++++---- 6 files changed, 115 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2e0f56de..da62f97d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" repository = "https://github.com/blackbeam/mysql_async" version = "0.34.0" exclude = ["test/*"] -edition = "2018" +edition = "2021" categories = ["asynchronous", "database"] [dependencies] diff --git a/src/error/mod.rs b/src/error/mod.rs index 983f274e..ffb788f8 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -8,7 +8,7 @@ pub use url::ParseError; -mod tls; +pub mod tls; use mysql_common::{ named_params::MixedParamsError, params::MissingNamedParameterError, diff --git a/src/error/tls/rustls_error.rs b/src/error/tls/rustls_error.rs index 2ee67d39..faae88db 100644 --- a/src/error/tls/rustls_error.rs +++ b/src/error/tls/rustls_error.rs @@ -2,11 +2,14 @@ use std::fmt::Display; +use rustls::server::VerifierBuilderError; + #[derive(Debug)] pub enum TlsError { Tls(rustls::Error), Pki(webpki::Error), InvalidDnsName(webpki::InvalidDnsNameError), + VerifierBuilderError(VerifierBuilderError), } impl From for crate::Error { @@ -15,6 +18,12 @@ impl From for crate::Error { } } +impl From for TlsError { + fn from(e: VerifierBuilderError) -> Self { + TlsError::VerifierBuilderError(e) + } +} + impl From for TlsError { fn from(e: rustls::Error) -> Self { TlsError::Tls(e) @@ -57,6 +66,7 @@ impl std::error::Error for TlsError { TlsError::Tls(e) => Some(e), TlsError::Pki(e) => Some(e), TlsError::InvalidDnsName(e) => Some(e), + TlsError::VerifierBuilderError(e) => Some(e), } } } @@ -67,6 +77,7 @@ impl Display for TlsError { TlsError::Tls(e) => e.fmt(f), TlsError::Pki(e) => e.fmt(f), TlsError::InvalidDnsName(e) => e.fmt(f), + TlsError::VerifierBuilderError(e) => e.fmt(f), } } } diff --git a/src/io/tls/rustls_io.rs b/src/io/tls/rustls_io.rs index e8757d0b..76080ff2 100644 --- a/src/io/tls/rustls_io.rs +++ b/src/io/tls/rustls_io.rs @@ -1,31 +1,35 @@ #![cfg(feature = "rustls-tls")] -use std::{convert::TryInto, sync::Arc}; +use std::sync::Arc; use rustls::{ - client::{ServerCertVerifier, WebPkiVerifier}, - Certificate, ClientConfig, OwnedTrustAnchor, RootCertStore, + client::{ + danger::{ServerCertVerified, ServerCertVerifier}, + WebPkiServerVerifier, + }, + pki_types::{CertificateDer, ServerName}, + ClientConfig, RootCertStore, }; use rustls_pemfile::certs; use tokio_rustls::TlsConnector; -use crate::{io::Endpoint, Result, SslOpts}; +use crate::{io::Endpoint, Result, SslOpts, TlsError}; impl SslOpts { - async fn load_root_certs(&self) -> crate::Result> { + async fn load_root_certs(&self) -> crate::Result>> { let mut output = Vec::new(); for root_cert in self.root_certs() { let root_cert_data = root_cert.read().await?; let mut seen = false; - for cert in certs(&mut &*root_cert_data)? { + for cert in certs(&mut &*root_cert_data) { seen = true; - output.push(Certificate(cert)); + output.push(cert?); } if !seen && !root_cert_data.is_empty() { - output.push(Certificate(root_cert_data.into_owned())); + output.push(CertificateDer::from(root_cert_data.into_owned())); } } @@ -42,21 +46,13 @@ impl Endpoint { } let mut root_store = RootCertStore::empty(); - root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); + root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|x| x.to_owned())); for cert in ssl_opts.load_root_certs().await? { - root_store.add(&cert)?; + root_store.add(cert)?; } - let config_builder = ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(root_store.clone()); + let config_builder = ClientConfig::builder().with_root_certificates(root_store.clone()); let mut config = if let Some(identity) = ssl_opts.client_identity() { let (cert_chain, priv_key) = identity.load().await?; @@ -65,12 +61,13 @@ impl Endpoint { config_builder.with_no_client_auth() }; - let server_name = domain - .as_str() - .try_into() - .map_err(|_| webpki::InvalidDnsNameError)?; + let server_name = ServerName::try_from(domain.as_str()) + .map_err(|_| webpki::InvalidDnsNameError)? + .to_owned(); let mut dangerous = config.dangerous(); - let web_pki_verifier = WebPkiVerifier::new(root_store, None); + let web_pki_verifier = WebPkiServerVerifier::builder(Arc::new(root_store)) + .build() + .map_err(TlsError::from)?; let dangerous_verifier = DangerousVerifier::new( ssl_opts.accept_invalid_certs(), ssl_opts.skip_domain_validation(), @@ -97,17 +94,18 @@ impl Endpoint { } } +#[derive(Debug)] struct DangerousVerifier { accept_invalid_certs: bool, skip_domain_validation: bool, - verifier: WebPkiVerifier, + verifier: Arc, } impl DangerousVerifier { fn new( accept_invalid_certs: bool, skip_domain_validation: bool, - verifier: WebPkiVerifier, + verifier: Arc, ) -> Self { Self { accept_invalid_certs, @@ -118,23 +116,51 @@ impl DangerousVerifier { } impl ServerCertVerifier for DangerousVerifier { + // fn verify_server_cert( + // &self, + // end_entity: &Certificate, + // intermediates: &[Certificate], + // server_name: &rustls::ServerName, + // scts: &mut dyn Iterator, + // ocsp_response: &[u8], + // now: std::time::SystemTime, + // ) -> std::result::Result { + // if self.accept_invalid_certs { + // Ok(rustls::client::ServerCertVerified::assertion()) + // } else { + // match self.verifier.verify_server_cert( + // end_entity, + // intermediates, + // server_name, + // scts, + // ocsp_response, + // now, + // ) { + // Ok(assertion) => Ok(assertion), + // Err(ref e) + // if e.to_string().contains("NotValidForName") && self.skip_domain_validation => + // { + // Ok(rustls::client::ServerCertVerified::assertion()) + // } + // Err(e) => Err(e), + // } + // } + // } fn verify_server_cert( &self, - end_entity: &Certificate, - intermediates: &[Certificate], - server_name: &rustls::ServerName, - scts: &mut dyn Iterator, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &rustls::pki_types::ServerName<'_>, ocsp_response: &[u8], - now: std::time::SystemTime, - ) -> std::result::Result { + now: rustls::pki_types::UnixTime, + ) -> std::prelude::v1::Result { if self.accept_invalid_certs { - Ok(rustls::client::ServerCertVerified::assertion()) + Ok(ServerCertVerified::assertion()) } else { match self.verifier.verify_server_cert( end_entity, intermediates, server_name, - scts, ocsp_response, now, ) { @@ -142,10 +168,34 @@ impl ServerCertVerifier for DangerousVerifier { Err(ref e) if e.to_string().contains("NotValidForName") && self.skip_domain_validation => { - Ok(rustls::client::ServerCertVerified::assertion()) + Ok(ServerCertVerified::assertion()) } Err(e) => Err(e), } } } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> std::prelude::v1::Result + { + self.verifier.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> std::prelude::v1::Result + { + self.verifier.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.verifier.supported_verify_schemes() + } } diff --git a/src/lib.rs b/src/lib.rs index f0605910..3e2c7983 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -466,7 +466,8 @@ pub use self::conn::pool::Pool; #[doc(inline)] pub use self::error::{ - DriverError, Error, IoError, LocalInfileError, ParseError, Result, ServerError, UrlError, + tls::TlsError, DriverError, Error, IoError, LocalInfileError, ParseError, Result, ServerError, + UrlError, }; #[doc(inline)] diff --git a/src/opts/rustls_opts.rs b/src/opts/rustls_opts.rs index 562846d8..ef954ea9 100644 --- a/src/opts/rustls_opts.rs +++ b/src/opts/rustls_opts.rs @@ -1,6 +1,6 @@ #![cfg(feature = "rustls-tls")] -use rustls::{Certificate, PrivateKey}; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs1KeyDer}; use rustls_pemfile::{certs, rsa_private_keys}; use std::{borrow::Cow, path::Path}; @@ -50,27 +50,31 @@ impl ClientIdentity { self.priv_key.borrow() } - pub(crate) async fn load(&self) -> crate::Result<(Vec, PrivateKey)> { + pub(crate) async fn load( + &self, + ) -> crate::Result<(Vec>, PrivateKeyDer<'static>)> { let cert_data = self.cert_chain.read().await?; let key_data = self.priv_key.read().await?; let mut cert_chain = Vec::new(); if std::str::from_utf8(&cert_data).is_err() { - cert_chain.push(Certificate(cert_data.into_owned())); + cert_chain.push(CertificateDer::from(cert_data.into_owned())); } else { - for cert in certs(&mut &*cert_data)? { - cert_chain.push(Certificate(cert)); + for cert in certs(&mut &*cert_data) { + cert_chain.push(cert?); } } let priv_key = if std::str::from_utf8(&key_data).is_err() { - Some(PrivateKey(key_data.into_owned())) + Some(PrivateKeyDer::Pkcs1(PrivatePkcs1KeyDer::from( + key_data.into_owned(), + ))) } else { - rsa_private_keys(&mut &*key_data)? - .into_iter() - .take(1) - .map(PrivateKey) - .next() + let mut priv_key = None; + for key in rsa_private_keys(&mut &*key_data).take(1) { + priv_key = Some(PrivateKeyDer::Pkcs1(key?.clone_key())); + } + priv_key }; Ok(( From 9d5ced305afaa5c2e6decb5d190db4107f6bceea Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 19 Mar 2024 10:03:58 +0300 Subject: [PATCH 7/7] Fix should_change_user for mysql 5.6 --- src/conn/mod.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/conn/mod.rs b/src/conn/mod.rs index 8d58f72b..7457f3fd 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -1547,7 +1547,7 @@ mod test { &["mysql_native_password"] }; - for plugin in plugins { + for (i, plugin) in plugins.iter().enumerate() { let mut rng = rand::thread_rng(); let mut pass = [0u8; 10]; pass.try_fill(&mut rng).unwrap(); @@ -1555,9 +1555,15 @@ mod test { .map(|x| ((x % (123 - 97)) + 97) as char) .collect(); - conn.query_drop("DROP USER /*!50700 IF EXISTS */ /*M!100103 IF EXISTS */ __mats") - .await - .unwrap(); + let result = conn + .query_drop("DROP USER /*!50700 IF EXISTS */ /*M!100103 IF EXISTS */ __mats") + .await; + if matches!(conn.server_version(), (5, 6, _)) && i == 0 { + // IF EXISTS is not supported on 5.6 so the query will fail on the first iteration + drop(result); + } else { + result.unwrap(); + } if conn.inner.is_mariadb || conn.server_version() < (5, 7, 0) { if matches!(conn.server_version(), (5, 6, _)) {