Skip to content

Commit

Permalink
Merge pull request #2491 from p-alik/issue-786-mysql-ssl-support
Browse files Browse the repository at this point in the history
Issue 786 mysql ssl support
  • Loading branch information
weiznich authored Feb 1, 2022
2 parents dd5f41d + 9dc434e commit 41404e6
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 2 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/

### Added

* `MysqlConnection::establish` is able to initiate SSL connection. The database URL should contain `ssl_mode` parameter with a value of the [MySQL client command option `--ssl-mode`](https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode) if desired.

* `Connection` and `SimpleConnection` traits are implemented for a broader range
of `r2d2::PooledConnection<M>` types when the `r2d2` feature is enabled.

Expand Down Expand Up @@ -80,7 +82,7 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
* Support for `uuid` version < 0.7.0 has been removed.
* Support for `bigdecimal` < 0.0.13 has been removed.
* Support for `pq-sys` < 0.4.0 has been removed.
* Support for `mysqlclient-sys` < 0.2.0 has been removed.
* Support for `mysqlclient-sys` < 0.2.5 has been removed.
* Support for `time` types has been removed.
* Support for `chrono` < 0.4.19 has been removed.
* The `NonNull` trait for sql types has been removed in favour of the new `SqlType` trait.
Expand Down
2 changes: 1 addition & 1 deletion diesel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ byteorder = { version = "1.0", optional = true }
chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] }
libc = { version = "0.2.0", optional = true }
libsqlite3-sys = { version = ">=0.17.2, <0.24.0", optional = true, features = ["bundled_bindings"] }
mysqlclient-sys = { version = "0.2.0", optional = true }
mysqlclient-sys = { version = "0.2.5", optional = true }
pq-sys = { version = "0.4.0", optional = true }
quickcheck = { version = "1.0.3", optional = true }
serde_json = { version = ">=0.8.0, <2.0", optional = true }
Expand Down
7 changes: 7 additions & 0 deletions diesel/src/mysql/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ impl Connection for MysqlConnection {
type Backend = Mysql;
type TransactionManager = AnsiTransactionManager;

/// Establishes a new connection to the MySQL database
/// `database_url` may be enhanced by GET parameters
/// `mysql://[user[:password]@]host/database_name[?unix_socket=socket-path&ssl_mode=SSL_MODE*]`
///
/// * `unix_socket` excepts the path to the unix socket
/// * `ssl_mode` expects a value defined for MySQL client command option `--ssl-mode`
/// See <https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode>
fn establish(database_url: &str) -> ConnectionResult<Self> {
use crate::result::ConnectionError::CouldntSetupConfiguration;

Expand Down
17 changes: 17 additions & 0 deletions diesel/src/mysql/connection/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ impl RawConnection {
let unix_socket = connection_options.unix_socket();
let client_flags = connection_options.client_flags();

if let Some(ssl_mode) = connection_options.ssl_mode() {
self.set_ssl_mode(ssl_mode)
}

unsafe {
// Make sure you don't use the fake one!
ffi::mysql_real_connect(
Expand Down Expand Up @@ -180,6 +184,19 @@ impl RawConnection {
unsafe { ffi::mysql_next_result(self.0.as_ptr()) };
self.did_an_error_occur()
}

fn set_ssl_mode(&self, ssl_mode: mysqlclient_sys::mysql_ssl_mode) {
let v = ssl_mode as u32;
let v_ptr: *const u32 = &v;
let n = ptr::NonNull::new(v_ptr as *mut u32).expect("NonNull::new failed");
unsafe {
mysqlclient_sys::mysql_options(
self.0.as_ptr(),
mysqlclient_sys::mysql_option::MYSQL_OPT_SSL_MODE,
n.as_ptr() as *const std::ffi::c_void,
)
};
}
}

impl Drop for RawConnection {
Expand Down
52 changes: 52 additions & 0 deletions diesel/src/mysql/connection/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use std::ffi::{CStr, CString};

use crate::result::{ConnectionError, ConnectionResult};

use mysqlclient_sys::mysql_ssl_mode;

bitflags::bitflags! {
pub struct CapabilityFlags: u32 {
const CLIENT_LONG_PASSWORD = 0x00000001;
Expand Down Expand Up @@ -46,6 +48,7 @@ pub struct ConnectionOptions {
port: Option<u16>,
unix_socket: Option<CString>,
client_flags: CapabilityFlags,
ssl_mode: Option<mysql_ssl_mode>,
}

impl ConnectionOptions {
Expand Down Expand Up @@ -73,6 +76,24 @@ impl ConnectionOptions {
_ => None,
};

let ssl_mode = match query_pairs.get("ssl_mode") {
Some(v) => {
let ssl_mode = match v.to_lowercase().as_str() {
"disabled" => mysql_ssl_mode::SSL_MODE_DISABLED,
"preferred" => mysql_ssl_mode::SSL_MODE_PREFERRED,
"required" => mysql_ssl_mode::SSL_MODE_REQUIRED,
"verify_ca" => mysql_ssl_mode::SSL_MODE_VERIFY_CA,
"verify_identity" => mysql_ssl_mode::SSL_MODE_VERIFY_IDENTITY,
_ => {
let msg = "unknown ssl_mode";
return Err(ConnectionError::InvalidConnectionUrl(msg.into()));
}
};
Some(ssl_mode)
}
_ => None,
};

let host = match url.host() {
Some(Host::Ipv6(host)) => Some(CString::new(host.to_string())?),
Some(host) if host.to_string() == "localhost" && unix_socket != None => None,
Expand Down Expand Up @@ -101,6 +122,7 @@ impl ConnectionOptions {
port: url.port(),
unix_socket: unix_socket,
client_flags: client_flags,
ssl_mode: ssl_mode,
})
}

Expand Down Expand Up @@ -131,6 +153,10 @@ impl ConnectionOptions {
pub fn client_flags(&self) -> CapabilityFlags {
self.client_flags
}

pub fn ssl_mode(&self) -> Option<mysql_ssl_mode> {
self.ssl_mode
}
}

fn decode_into_cstring(s: &str) -> ConnectionResult<CString> {
Expand Down Expand Up @@ -266,3 +292,29 @@ fn unix_socket_tests() {
conn_opts.unix_socket.unwrap()
);
}

#[test]
fn ssl_mode() {
let ssl_mode = |url| ConnectionOptions::parse(url).unwrap().ssl_mode();
assert_eq!(ssl_mode("mysql://localhost"), None);
assert_eq!(
ssl_mode("mysql://localhost?ssl_mode=disabled"),
Some(mysql_ssl_mode::SSL_MODE_DISABLED)
);
assert_eq!(
ssl_mode("mysql://localhost?ssl_mode=PREFERRED"),
Some(mysql_ssl_mode::SSL_MODE_PREFERRED)
);
assert_eq!(
ssl_mode("mysql://localhost?ssl_mode=required"),
Some(mysql_ssl_mode::SSL_MODE_REQUIRED)
);
assert_eq!(
ssl_mode("mysql://localhost?ssl_mode=VERIFY_CA"),
Some(mysql_ssl_mode::SSL_MODE_VERIFY_CA)
);
assert_eq!(
ssl_mode("mysql://localhost?ssl_mode=verify_identity"),
Some(mysql_ssl_mode::SSL_MODE_VERIFY_IDENTITY)
);
}

0 comments on commit 41404e6

Please sign in to comment.