sqlx_mysql/connection/
tls.rs

1use crate::collation::{CharSet, Collation};
2use crate::connection::{MySqlStream, Waiting};
3use crate::error::Error;
4use crate::net::tls::TlsConfig;
5use crate::net::{tls, BufferedSocket, Socket, WithSocket};
6use crate::protocol::connect::SslRequest;
7use crate::protocol::Capabilities;
8use crate::{MySqlConnectOptions, MySqlSslMode};
9use std::collections::VecDeque;
10
11struct MapStream {
12    server_version: (u16, u16, u16),
13    capabilities: Capabilities,
14    sequence_id: u8,
15    waiting: VecDeque<Waiting>,
16    charset: CharSet,
17    collation: Collation,
18}
19
20pub(super) async fn maybe_upgrade<S: Socket>(
21    mut stream: MySqlStream<S>,
22    options: &MySqlConnectOptions,
23) -> Result<MySqlStream, Error> {
24    let server_supports_tls = stream.capabilities.contains(Capabilities::SSL);
25
26    if matches!(options.ssl_mode, MySqlSslMode::Disabled) || !tls::available() {
27        // remove the SSL capability if SSL has been explicitly disabled
28        stream.capabilities.remove(Capabilities::SSL);
29    }
30
31    // https://www.postgresql.org/docs/12/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS
32    match options.ssl_mode {
33        MySqlSslMode::Disabled => return Ok(stream.boxed_socket()),
34
35        MySqlSslMode::Preferred => {
36            if !tls::available() {
37                // Client doesn't support TLS
38                tracing::debug!("not performing TLS upgrade: TLS support not compiled in");
39                return Ok(stream.boxed_socket());
40            }
41
42            if !server_supports_tls {
43                // Server doesn't support TLS
44                tracing::debug!("not performing TLS upgrade: unsupported by server");
45                return Ok(stream.boxed_socket());
46            }
47        }
48
49        MySqlSslMode::Required | MySqlSslMode::VerifyIdentity | MySqlSslMode::VerifyCa => {
50            tls::error_if_unavailable()?;
51
52            if !server_supports_tls {
53                // upgrade failed, die
54                return Err(Error::Tls("server does not support TLS".into()));
55            }
56        }
57    }
58
59    let tls_config = TlsConfig {
60        accept_invalid_certs: !matches!(
61            options.ssl_mode,
62            MySqlSslMode::VerifyCa | MySqlSslMode::VerifyIdentity
63        ),
64        accept_invalid_hostnames: !matches!(options.ssl_mode, MySqlSslMode::VerifyIdentity),
65        hostname: &options.host,
66        root_cert_path: options.ssl_ca.as_ref(),
67        client_cert_path: options.ssl_client_cert.as_ref(),
68        client_key_path: options.ssl_client_key.as_ref(),
69    };
70
71    // Request TLS upgrade
72    stream.write_packet(SslRequest {
73        max_packet_size: super::MAX_PACKET_SIZE,
74        collation: stream.collation as u8,
75    })?;
76
77    stream.flush().await?;
78
79    tls::handshake(
80        stream.socket.into_inner(),
81        tls_config,
82        MapStream {
83            server_version: stream.server_version,
84            capabilities: stream.capabilities,
85            sequence_id: stream.sequence_id,
86            waiting: stream.waiting,
87            charset: stream.charset,
88            collation: stream.collation,
89        },
90    )
91    .await
92}
93
94impl WithSocket for MapStream {
95    type Output = MySqlStream;
96
97    async fn with_socket<S: Socket>(self, socket: S) -> Self::Output {
98        MySqlStream {
99            socket: BufferedSocket::new(Box::new(socket)),
100            server_version: self.server_version,
101            capabilities: self.capabilities,
102            sequence_id: self.sequence_id,
103            waiting: self.waiting,
104            charset: self.charset,
105            collation: self.collation,
106            is_tls: true,
107        }
108    }
109}