| use crate::msgs::enums::CipherSuite; |
| use crate::msgs::enums::{AlertDescription, HandshakeType}; |
| use crate::session::{Session, SessionCommon}; |
| use crate::keylog::{KeyLog, NoKeyLog}; |
| use crate::suites::{SupportedCipherSuite, ALL_CIPHERSUITES}; |
| use crate::msgs::handshake::CertificatePayload; |
| use crate::msgs::enums::SignatureScheme; |
| use crate::msgs::enums::{ContentType, ProtocolVersion}; |
| use crate::msgs::handshake::ClientExtension; |
| use crate::msgs::message::Message; |
| use crate::verify; |
| use crate::anchors; |
| use crate::sign; |
| use crate::error::TLSError; |
| use crate::key; |
| use crate::vecbuf::WriteV; |
| #[cfg(feature = "logging")] |
| use crate::log::trace; |
| |
| use std::sync::Arc; |
| use std::io; |
| use std::fmt; |
| use std::mem; |
| |
| use sct; |
| use webpki; |
| |
| #[macro_use] |
| mod hs; |
| mod tls12; |
| mod tls13; |
| mod common; |
| pub mod handy; |
| |
| /// A trait for the ability to store client session data. |
| /// The keys and values are opaque. |
| /// |
| /// Both the keys and values should be treated as |
| /// **highly sensitive data**, containing enough key material |
| /// to break all security of the corresponding session. |
| /// |
| /// `put` is a mutating operation; this isn't expressed |
| /// in the type system to allow implementations freedom in |
| /// how to achieve interior mutability. `Mutex` is a common |
| /// choice. |
| pub trait StoresClientSessions : Send + Sync { |
| /// Stores a new `value` for `key`. Returns `true` |
| /// if the value was stored. |
| fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool; |
| |
| /// Returns the latest value for `key`. Returns `None` |
| /// if there's no such value. |
| fn get(&self, key: &[u8]) -> Option<Vec<u8>>; |
| } |
| |
| /// A trait for the ability to choose a certificate chain and |
| /// private key for the purposes of client authentication. |
| pub trait ResolvesClientCert : Send + Sync { |
| /// With the server-supplied acceptable issuers in `acceptable_issuers`, |
| /// the server's supported signature schemes in `sigschemes`, |
| /// return a certificate chain and signing key to authenticate. |
| /// |
| /// `acceptable_issuers` is undecoded and unverified by the rustls |
| /// library, but it should be expected to contain a DER encodings |
| /// of X501 NAMEs. |
| /// |
| /// Return None to continue the handshake without any client |
| /// authentication. The server may reject the handshake later |
| /// if it requires authentication. |
| fn resolve(&self, |
| acceptable_issuers: &[&[u8]], |
| sigschemes: &[SignatureScheme]) |
| -> Option<sign::CertifiedKey>; |
| |
| /// Return true if any certificates at all are available. |
| fn has_certs(&self) -> bool; |
| } |
| |
| /// Common configuration for (typically) all connections made by |
| /// a program. |
| /// |
| /// Making one of these can be expensive, and should be |
| /// once per process rather than once per connection. |
| #[derive(Clone)] |
| pub struct ClientConfig { |
| /// List of ciphersuites, in preference order. |
| pub ciphersuites: Vec<&'static SupportedCipherSuite>, |
| |
| /// Collection of root certificates. |
| pub root_store: anchors::RootCertStore, |
| |
| /// Which ALPN protocols we include in our client hello. |
| /// If empty, no ALPN extension is sent. |
| pub alpn_protocols: Vec<Vec<u8>>, |
| |
| /// How we store session data or tickets. |
| pub session_persistence: Arc<dyn StoresClientSessions>, |
| |
| /// Our MTU. If None, we don't limit TLS message sizes. |
| pub mtu: Option<usize>, |
| |
| /// How to decide what client auth certificate/keys to use. |
| pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>, |
| |
| /// Whether to support RFC5077 tickets. You must provide a working |
| /// `session_persistence` member for this to have any meaningful |
| /// effect. |
| /// |
| /// The default is true. |
| pub enable_tickets: bool, |
| |
| /// Supported versions, in no particular order. The default |
| /// is all supported versions. |
| pub versions: Vec<ProtocolVersion>, |
| |
| /// Collection of certificate transparency logs. |
| /// If this collection is empty, then certificate transparency |
| /// checking is disabled. |
| pub ct_logs: Option<&'static [&'static sct::Log<'static>]>, |
| |
| /// Whether to send the Server Name Indication (SNI) extension |
| /// during the client handshake. |
| /// |
| /// The default is true. |
| pub enable_sni: bool, |
| |
| /// How to verify the server certificate chain. |
| verifier: Arc<dyn verify::ServerCertVerifier>, |
| |
| /// How to output key material for debugging. The default |
| /// does nothing. |
| pub key_log: Arc<dyn KeyLog>, |
| |
| /// Whether to send data on the first flight ("early data") in |
| /// TLS 1.3 handshakes. |
| /// |
| /// The default is false. |
| pub enable_early_data: bool, |
| } |
| |
| impl Default for ClientConfig { |
| fn default() -> Self { Self::new() } |
| } |
| |
| impl ClientConfig { |
| /// Make a `ClientConfig` with a default set of ciphersuites, |
| /// no root certificates, no ALPN protocols, and no client auth. |
| /// |
| /// The default session persistence provider stores up to 32 |
| /// items in memory. |
| pub fn new() -> ClientConfig { |
| ClientConfig { |
| ciphersuites: ALL_CIPHERSUITES.to_vec(), |
| root_store: anchors::RootCertStore::empty(), |
| alpn_protocols: Vec::new(), |
| session_persistence: handy::ClientSessionMemoryCache::new(32), |
| mtu: None, |
| client_auth_cert_resolver: Arc::new(handy::FailResolveClientCert {}), |
| enable_tickets: true, |
| versions: vec![ProtocolVersion::TLSv1_3, ProtocolVersion::TLSv1_2], |
| ct_logs: None, |
| enable_sni: true, |
| verifier: Arc::new(verify::WebPKIVerifier::new()), |
| key_log: Arc::new(NoKeyLog {}), |
| enable_early_data: false, |
| } |
| } |
| |
| #[doc(hidden)] |
| /// We support a given TLS version if it's quoted in the configured |
| /// versions *and* at least one ciphersuite for this version is |
| /// also configured. |
| pub fn supports_version(&self, v: ProtocolVersion) -> bool { |
| self.versions.contains(&v) && self.ciphersuites.iter().any(|cs| cs.usable_for_version(v)) |
| } |
| |
| #[doc(hidden)] |
| pub fn get_verifier(&self) -> &dyn verify::ServerCertVerifier { |
| self.verifier.as_ref() |
| } |
| |
| /// Set the ALPN protocol list to the given protocol names. |
| /// Overwrites any existing configured protocols. |
| /// The first element in the `protocols` list is the most |
| /// preferred, the last is the least preferred. |
| pub fn set_protocols(&mut self, protocols: &[Vec<u8>]) { |
| self.alpn_protocols.clear(); |
| self.alpn_protocols.extend_from_slice(protocols); |
| } |
| |
| /// Sets persistence layer to `persist`. |
| pub fn set_persistence(&mut self, persist: Arc<dyn StoresClientSessions>) { |
| self.session_persistence = persist; |
| } |
| |
| /// Sets MTU to `mtu`. If None, the default is used. |
| /// If Some(x) then x must be greater than 5 bytes. |
| pub fn set_mtu(&mut self, mtu: &Option<usize>) { |
| // Internally our MTU relates to fragment size, and does |
| // not include the TLS header overhead. |
| // |
| // Externally the MTU is the whole packet size. The difference |
| // is PACKET_OVERHEAD. |
| if let Some(x) = *mtu { |
| use crate::msgs::fragmenter; |
| debug_assert!(x > fragmenter::PACKET_OVERHEAD); |
| self.mtu = Some(x - fragmenter::PACKET_OVERHEAD); |
| } else { |
| self.mtu = None; |
| } |
| } |
| |
| /// Sets a single client authentication certificate and private key. |
| /// This is blindly used for all servers that ask for client auth. |
| /// |
| /// `cert_chain` is a vector of DER-encoded certificates, |
| /// `key_der` is a DER-encoded RSA or ECDSA private key. |
| pub fn set_single_client_cert(&mut self, |
| cert_chain: Vec<key::Certificate>, |
| key_der: key::PrivateKey) -> Result<(), TLSError> { |
| let resolver = handy::AlwaysResolvesClientCert::new(cert_chain, &key_der)?; |
| self.client_auth_cert_resolver = Arc::new(resolver); |
| Ok(()) |
| } |
| |
| /// Access configuration options whose use is dangerous and requires |
| /// extra care. |
| #[cfg(feature = "dangerous_configuration")] |
| pub fn dangerous(&mut self) -> danger::DangerousClientConfig { |
| danger::DangerousClientConfig { cfg: self } |
| } |
| } |
| |
| /// Container for unsafe APIs |
| #[cfg(feature = "dangerous_configuration")] |
| pub mod danger { |
| use std::sync::Arc; |
| |
| use super::ClientConfig; |
| use super::verify::ServerCertVerifier; |
| |
| /// Accessor for dangerous configuration options. |
| pub struct DangerousClientConfig<'a> { |
| /// The underlying ClientConfig |
| pub cfg: &'a mut ClientConfig |
| } |
| |
| impl<'a> DangerousClientConfig<'a> { |
| /// Overrides the default `ServerCertVerifier` with something else. |
| pub fn set_certificate_verifier(&mut self, |
| verifier: Arc<dyn ServerCertVerifier>) { |
| self.cfg.verifier = verifier; |
| } |
| } |
| } |
| |
| #[derive(Debug, PartialEq)] |
| enum EarlyDataState { |
| Disabled, |
| Ready, |
| Accepted, |
| AcceptedFinished, |
| Rejected, |
| } |
| |
| pub struct EarlyData { |
| state: EarlyDataState, |
| left: usize, |
| } |
| |
| impl EarlyData { |
| fn new() -> EarlyData { |
| EarlyData { |
| left: 0, |
| state: EarlyDataState::Disabled, |
| } |
| } |
| |
| fn is_enabled(&self) -> bool { |
| match self.state { |
| EarlyDataState::Ready | EarlyDataState::Accepted => true, |
| _ => false |
| } |
| } |
| |
| fn is_accepted(&self) -> bool { |
| match self.state { |
| EarlyDataState::Accepted | EarlyDataState::AcceptedFinished => true, |
| _ => false |
| } |
| } |
| |
| fn enable(&mut self, max_data: usize) { |
| assert_eq!(self.state, EarlyDataState::Disabled); |
| self.state = EarlyDataState::Ready; |
| self.left = max_data; |
| } |
| |
| fn rejected(&mut self) { |
| trace!("EarlyData rejected"); |
| self.state = EarlyDataState::Rejected; |
| } |
| |
| fn accepted(&mut self) { |
| trace!("EarlyData accepted"); |
| assert_eq!(self.state, EarlyDataState::Ready); |
| self.state = EarlyDataState::Accepted; |
| } |
| |
| fn finished(&mut self) { |
| trace!("EarlyData finished"); |
| self.state = match self.state { |
| EarlyDataState::Accepted => EarlyDataState::AcceptedFinished, |
| _ => panic!("bad EarlyData state"), |
| } |
| } |
| |
| fn check_write(&mut self, sz: usize) -> io::Result<usize> { |
| match self.state { |
| EarlyDataState::Disabled => unreachable!(), |
| EarlyDataState::Ready | EarlyDataState::Accepted => { |
| let take = if self.left < sz { |
| mem::replace(&mut self.left, 0) |
| } else { |
| self.left -= sz; |
| sz |
| }; |
| |
| Ok(take) |
| }, |
| EarlyDataState::Rejected |
| | EarlyDataState::AcceptedFinished => { |
| Err(io::Error::from(io::ErrorKind::InvalidInput)) |
| }, |
| } |
| } |
| |
| fn bytes_left(&self) -> usize { |
| self.left |
| } |
| } |
| |
| /// Stub that implements io::Write and dispatches to `write_early_data`. |
| pub struct WriteEarlyData<'a> { |
| sess: &'a mut ClientSessionImpl, |
| } |
| |
| impl<'a> WriteEarlyData<'a> { |
| fn new(sess: &'a mut ClientSessionImpl) -> WriteEarlyData<'a> { |
| WriteEarlyData { sess } |
| } |
| |
| /// How many bytes you may send. Writes will become short |
| /// once this reaches zero. |
| pub fn bytes_left(&self) -> usize { |
| self.sess.early_data.bytes_left() |
| } |
| } |
| |
| impl<'a> io::Write for WriteEarlyData<'a> { |
| fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| self.sess.write_early_data(buf) |
| } |
| |
| fn flush(&mut self) -> io::Result<()> { |
| Ok(()) |
| } |
| } |
| |
| pub struct ClientSessionImpl { |
| pub config: Arc<ClientConfig>, |
| pub alpn_protocol: Option<Vec<u8>>, |
| pub common: SessionCommon, |
| pub error: Option<TLSError>, |
| pub state: Option<Box<dyn hs::State + Send + Sync>>, |
| pub server_cert_chain: CertificatePayload, |
| pub early_data: EarlyData, |
| pub resumption_ciphersuite: Option<&'static SupportedCipherSuite>, |
| } |
| |
| impl fmt::Debug for ClientSessionImpl { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.debug_struct("ClientSessionImpl").finish() |
| } |
| } |
| |
| impl ClientSessionImpl { |
| pub fn new(config: &Arc<ClientConfig>) -> ClientSessionImpl { |
| ClientSessionImpl { |
| config: config.clone(), |
| alpn_protocol: None, |
| common: SessionCommon::new(config.mtu, true), |
| error: None, |
| state: None, |
| server_cert_chain: Vec::new(), |
| early_data: EarlyData::new(), |
| resumption_ciphersuite: None, |
| } |
| } |
| |
| pub fn start_handshake(&mut self, hostname: webpki::DNSName, extra_exts: Vec<ClientExtension>) { |
| self.state = Some(hs::start_handshake(self, hostname, extra_exts)); |
| } |
| |
| pub fn get_cipher_suites(&self) -> Vec<CipherSuite> { |
| let mut ret = Vec::new(); |
| |
| for cs in &self.config.ciphersuites { |
| ret.push(cs.suite); |
| } |
| |
| // We don't do renegotation at all, in fact. |
| ret.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); |
| |
| ret |
| } |
| |
| pub fn find_cipher_suite(&self, suite: CipherSuite) -> Option<&'static SupportedCipherSuite> { |
| for scs in &self.config.ciphersuites { |
| if scs.suite == suite { |
| return Some(scs); |
| } |
| } |
| |
| None |
| } |
| |
| pub fn wants_read(&self) -> bool { |
| // We want to read more data all the time, except when we |
| // have unprocessed plaintext. This provides back-pressure |
| // to the TCP buffers. |
| // |
| // This also covers the handshake case, because we don't have |
| // readable plaintext before handshake has completed. |
| !self.common.has_readable_plaintext() |
| } |
| |
| pub fn wants_write(&self) -> bool { |
| !self.common.sendable_tls.is_empty() |
| } |
| |
| pub fn is_handshaking(&self) -> bool { |
| !self.common.traffic |
| } |
| |
| pub fn set_buffer_limit(&mut self, len: usize) { |
| self.common.set_buffer_limit(len) |
| } |
| |
| pub fn process_msg(&mut self, mut msg: Message) -> Result<(), TLSError> { |
| // TLS1.3: drop CCS at any time during handshaking |
| if self.common.is_tls13() |
| && msg.is_content_type(ContentType::ChangeCipherSpec) |
| && self.is_handshaking() { |
| trace!("Dropping CCS"); |
| return Ok(()); |
| } |
| |
| // Decrypt if demanded by current state. |
| if self.common.record_layer.is_decrypting() { |
| let dm = self.common.decrypt_incoming(msg)?; |
| msg = dm; |
| } |
| |
| // For handshake messages, we need to join them before parsing |
| // and processing. |
| if self.common.handshake_joiner.want_message(&msg) { |
| self.common |
| .handshake_joiner |
| .take_message(msg) |
| .ok_or_else(|| { |
| self.common.send_fatal_alert(AlertDescription::DecodeError); |
| TLSError::CorruptMessagePayload(ContentType::Handshake) |
| })?; |
| return self.process_new_handshake_messages(); |
| } |
| |
| // Now we can fully parse the message payload. |
| if !msg.decode_payload() { |
| return Err(TLSError::CorruptMessagePayload(msg.typ)); |
| } |
| |
| // For alerts, we have separate logic. |
| if msg.is_content_type(ContentType::Alert) { |
| return self.common.process_alert(msg); |
| } |
| |
| self.process_main_protocol(msg) |
| } |
| |
| pub fn process_new_handshake_messages(&mut self) -> Result<(), TLSError> { |
| while let Some(msg) = self.common.handshake_joiner.frames.pop_front() { |
| self.process_main_protocol(msg)?; |
| } |
| |
| Ok(()) |
| } |
| |
| fn queue_unexpected_alert(&mut self) { |
| self.common.send_fatal_alert(AlertDescription::UnexpectedMessage); |
| } |
| |
| fn reject_renegotiation_attempt(&mut self) -> Result<(), TLSError> { |
| self.common.send_warning_alert(AlertDescription::NoRenegotiation); |
| Ok(()) |
| } |
| |
| /// Process `msg`. First, we get the current state. Then we ask what messages |
| /// that state expects, enforced via `check_message`. Finally, we ask the handler |
| /// to handle the message. |
| fn process_main_protocol(&mut self, msg: Message) -> Result<(), TLSError> { |
| // For TLS1.2, outside of the handshake, send rejection alerts for |
| // renegotation requests. These can occur any time. |
| if msg.is_handshake_type(HandshakeType::HelloRequest) && |
| !self.common.is_tls13() && |
| !self.is_handshaking() { |
| return self.reject_renegotiation_attempt(); |
| } |
| |
| let state = self.state.take().unwrap(); |
| state |
| .check_message(&msg) |
| .map_err(|err| { |
| self.queue_unexpected_alert(); |
| err |
| })?; |
| self.state = Some(state.handle(self, msg)?); |
| |
| Ok(()) |
| } |
| |
| pub fn process_new_packets(&mut self) -> Result<(), TLSError> { |
| if let Some(ref err) = self.error { |
| return Err(err.clone()); |
| } |
| |
| if self.common.message_deframer.desynced { |
| return Err(TLSError::CorruptMessage); |
| } |
| |
| while let Some(msg) = self.common.message_deframer.frames.pop_front() { |
| match self.process_msg(msg) { |
| Ok(_) => {} |
| Err(err) => { |
| self.error = Some(err.clone()); |
| return Err(err); |
| } |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| pub fn get_peer_certificates(&self) -> Option<Vec<key::Certificate>> { |
| if self.server_cert_chain.is_empty() { |
| return None; |
| } |
| |
| let mut r = Vec::new(); |
| for cert in &self.server_cert_chain { |
| r.push(cert.clone()); |
| } |
| |
| Some(r) |
| } |
| |
| pub fn get_alpn_protocol(&self) -> Option<&[u8]> { |
| self.alpn_protocol.as_ref().map(AsRef::as_ref) |
| } |
| |
| pub fn get_protocol_version(&self) -> Option<ProtocolVersion> { |
| self.common.negotiated_version |
| } |
| |
| pub fn get_negotiated_ciphersuite(&self) -> Option<&'static SupportedCipherSuite> { |
| self.common.get_suite() |
| } |
| |
| pub fn write_early_data(&mut self, data: &[u8]) -> io::Result<usize> { |
| self.early_data.check_write(data.len()) |
| .and_then(|sz| { |
| self.common.send_early_plaintext(&data[..sz]) |
| }) |
| } |
| |
| fn export_keying_material(&self, |
| output: &mut [u8], |
| label: &[u8], |
| context: Option<&[u8]>) -> Result<(), TLSError> { |
| self.state |
| .as_ref() |
| .ok_or_else(|| TLSError::HandshakeNotComplete) |
| .and_then(|st| st.export_keying_material(output, label, context)) |
| } |
| |
| fn send_some_plaintext(&mut self, buf: &[u8]) -> io::Result<usize> { |
| let mut st = self.state.take(); |
| st.as_mut() |
| .map(|st| st.perhaps_write_key_update(self)); |
| self.state = st; |
| |
| self.common.send_some_plaintext(buf) |
| } |
| } |
| |
| /// This represents a single TLS client session. |
| #[derive(Debug)] |
| pub struct ClientSession { |
| // We use the pimpl idiom to hide unimportant details. |
| pub(crate) imp: ClientSessionImpl, |
| } |
| |
| impl ClientSession { |
| /// Make a new ClientSession. `config` controls how |
| /// we behave in the TLS protocol, `hostname` is the |
| /// hostname of who we want to talk to. |
| pub fn new(config: &Arc<ClientConfig>, hostname: webpki::DNSNameRef) -> ClientSession { |
| let mut imp = ClientSessionImpl::new(config); |
| imp.start_handshake(hostname.into(), vec![]); |
| ClientSession { imp } |
| } |
| |
| /// Returns an `io::Write` implementor you can write bytes to |
| /// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server. |
| /// |
| /// This returns None in many circumstances when the capability to |
| /// send early data is not available, including but not limited to: |
| /// |
| /// - The server hasn't been talked to previously. |
| /// - The server does not support resumption. |
| /// - The server does not support early data. |
| /// - The resumption data for the server has expired. |
| /// |
| /// The server specifies a maximum amount of early data. You can |
| /// learn this limit through the returned object, and writes through |
| /// it will process only this many bytes. |
| /// |
| /// The server can choose not to accept any sent early data -- |
| /// in this case the data is lost but the connection continues. You |
| /// can tell this happened using `is_early_data_accepted`. |
| pub fn early_data(&mut self) -> Option<WriteEarlyData> { |
| if self.imp.early_data.is_enabled() { |
| Some(WriteEarlyData::new(&mut self.imp)) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns True if the server signalled it will process early data. |
| /// |
| /// If you sent early data and this returns false at the end of the |
| /// handshake then the server will not process the data. This |
| /// is not an error, but you may wish to resend the data. |
| pub fn is_early_data_accepted(&self) -> bool { |
| self.imp.early_data.is_accepted() |
| } |
| } |
| |
| impl Session for ClientSession { |
| fn read_tls(&mut self, rd: &mut dyn io::Read) -> io::Result<usize> { |
| self.imp.common.read_tls(rd) |
| } |
| |
| /// Writes TLS messages to `wr`. |
| fn write_tls(&mut self, wr: &mut dyn io::Write) -> io::Result<usize> { |
| self.imp.common.write_tls(wr) |
| } |
| |
| fn writev_tls(&mut self, wr: &mut dyn WriteV) -> io::Result<usize> { |
| self.imp.common.writev_tls(wr) |
| } |
| |
| fn process_new_packets(&mut self) -> Result<(), TLSError> { |
| self.imp.process_new_packets() |
| } |
| |
| fn wants_read(&self) -> bool { |
| self.imp.wants_read() |
| } |
| |
| fn wants_write(&self) -> bool { |
| self.imp.wants_write() |
| } |
| |
| fn is_handshaking(&self) -> bool { |
| self.imp.is_handshaking() |
| } |
| |
| fn set_buffer_limit(&mut self, len: usize) { |
| self.imp.set_buffer_limit(len) |
| } |
| |
| fn send_close_notify(&mut self) { |
| self.imp.common.send_close_notify() |
| } |
| |
| fn get_peer_certificates(&self) -> Option<Vec<key::Certificate>> { |
| self.imp.get_peer_certificates() |
| } |
| |
| fn get_alpn_protocol(&self) -> Option<&[u8]> { |
| self.imp.get_alpn_protocol() |
| } |
| |
| fn get_protocol_version(&self) -> Option<ProtocolVersion> { |
| self.imp.get_protocol_version() |
| } |
| |
| fn export_keying_material(&self, |
| output: &mut [u8], |
| label: &[u8], |
| context: Option<&[u8]>) -> Result<(), TLSError> { |
| self.imp.export_keying_material(output, label, context) |
| } |
| |
| fn get_negotiated_ciphersuite(&self) -> Option<&'static SupportedCipherSuite> { |
| self.imp.get_negotiated_ciphersuite().or(self.imp.resumption_ciphersuite) |
| } |
| |
| } |
| |
| impl io::Read for ClientSession { |
| /// Obtain plaintext data received from the peer over |
| /// this TLS connection. |
| fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
| self.imp.common.read(buf) |
| } |
| } |
| |
| impl io::Write for ClientSession { |
| /// Send the plaintext `buf` to the peer, encrypting |
| /// and authenticating it. Once this function succeeds |
| /// you should call `write_tls` which will output the |
| /// corresponding TLS records. |
| /// |
| /// This function buffers plaintext sent before the |
| /// TLS handshake completes, and sends it as soon |
| /// as it can. This buffer is of *unlimited size* so |
| /// writing much data before it can be sent will |
| /// cause excess memory usage. |
| fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| self.imp.send_some_plaintext(buf) |
| } |
| |
| fn flush(&mut self) -> io::Result<()> { |
| self.imp.common.flush_plaintext(); |
| Ok(()) |
| } |
| } |