Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ jobs:
run: cargo build --no-default-features --features ssl-openssl
if: runner.os == 'Linux'

- name: Test vendored OpenSSL build
run: cargo build --no-default-features --features ssl-vendor
if: runner.os == 'Linux'

# - name: Install tk-dev for tkinter build
# run: sudo apt-get update && sudo apt-get install -y tk-dev
# if: runner.os == 'Linux'
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
flamescope = { version = "0.1.2", optional = true }

rustls = { workspace = true, optional = true }
rustls-graviola = { workspace = true, optional = true }

Check warning on line 52 in Cargo.toml

View workflow job for this annotation

GitHub Actions / cargo shear

shear/misplaced_optional_dependency

misplaced optional dependency `rustls-graviola` (remove the `optional` flag and move to `[dev-dependencies]`)

[target.'cfg(windows)'.dependencies]
libc = { workspace = true }
Expand Down Expand Up @@ -217,6 +217,7 @@
flame = "0.2.2"
flamer = "0.5"
flate2 = { version = "1.1.9", default-features = false }
# Bump only when the openssl crate bumps it
foreign-types-shared = "0.1"
gethostname = "1.0.2"
getrandom = { version = "0.3", features = ["std"] }
Expand Down
64 changes: 31 additions & 33 deletions crates/stdlib/src/openssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ mod _ssl {
LazyLock, PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard,
PyRwLockWriteGuard,
},
socket::{self, PySocket},
socket::{self, PySocket, SockWaitKind, sock_wait},
vm::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{
Expand Down Expand Up @@ -2297,49 +2297,47 @@ mod _ssl {
needs: SslNeeds,
deadline: &SocketDeadline,
vm: &VirtualMachine,
) -> SelectRet {
let sock = match self.0.sock_opt() {
Some(s) => s,
None => return SelectRet::Closed,
) -> PyResult<SelectRet> {
let Some(sock) = self.0.sock_opt() else {
return Ok(SelectRet::Closed);
};
// For blocking sockets without timeout, call sock_wait with None timeout
// For blocking sockets without timeout, call sock_select with None timeout
// to actually block waiting for data instead of busy-looping
let timeout = match &deadline {
Ok(deadline) => match deadline.checked_duration_since(Instant::now()) {
Some(d) => Some(d),
None => return SelectRet::TimedOut,
None => return Ok(SelectRet::TimedOut),
},
Err(true) => None, // Blocking: no timeout, wait indefinitely
Err(false) => return SelectRet::Nonblocking,
Err(false) => return Ok(SelectRet::Nonblocking),
};
let res = socket::sock_wait(
&sock,
match needs {
SslNeeds::Read => socket::SockWaitKind::Read,
SslNeeds::Write => socket::SockWaitKind::Write,
},
timeout,
vm,
);
match res {
Ok(true) => SelectRet::TimedOut,
_ => SelectRet::Ok,
}
let wait_kind = match needs {
SslNeeds::Read => SockWaitKind::Read,
SslNeeds::Write => SockWaitKind::Write,
};
sock_wait(&*sock, wait_kind, timeout, vm).map(|timed_out| {
if timed_out {
SelectRet::TimedOut
} else {
SelectRet::Ok
}
})
}

fn socket_needs(
&self,
err: &ssl::Error,
deadline: &SocketDeadline,
vm: &VirtualMachine,
) -> (Option<SslNeeds>, SelectRet) {
) -> PyResult<(Option<SslNeeds>, SelectRet)> {
let needs = match err.code() {
ssl::ErrorCode::WANT_READ => Some(SslNeeds::Read),
ssl::ErrorCode::WANT_WRITE => Some(SslNeeds::Write),
_ => None,
};
let state = needs.map_or(SelectRet::Ok, |needs| self.select(needs, deadline, vm));
(needs, state)
let state =
needs.map_or(Ok(SelectRet::Ok), |needs| self.select(needs, deadline, vm))?;
Ok((needs, state))
}
}

Expand Down Expand Up @@ -2857,7 +2855,7 @@ mod _ssl {
break;
}
// Wait briefly for peer's close_notify before retrying
match socket_stream.select(SslNeeds::Read, &deadline, vm) {
match socket_stream.select(SslNeeds::Read, &deadline, vm)? {
SelectRet::TimedOut => {
return Err(socket::timeout_error_msg(
vm,
Expand Down Expand Up @@ -2895,7 +2893,7 @@ mod _ssl {
};

// Wait on the socket
match socket_stream.select(needs, &deadline, vm) {
match socket_stream.select(needs, &deadline, vm)? {
SelectRet::TimedOut => {
let msg = if err == sys::SSL_ERROR_WANT_READ {
"The read operation timed out"
Expand Down Expand Up @@ -2991,7 +2989,7 @@ mod _ssl {
let (needs, state) = stream
.get_ref()
.expect("handshake called in bio mode; should only be called in socket mode")
.socket_needs(&err, &timeout, vm);
.socket_needs(&err, &timeout, vm)?;
match state {
SelectRet::TimedOut => {
// Clean up SNI ex_data before returning error
Expand Down Expand Up @@ -3045,7 +3043,7 @@ mod _ssl {
.get_ref()
.expect("write called in bio mode; should only be called in socket mode");
let timeout = socket_ref.timeout_deadline();
let state = socket_ref.select(SslNeeds::Write, &timeout, vm);
let state = socket_ref.select(SslNeeds::Write, &timeout, vm)?;
match state {
SelectRet::TimedOut => {
return Err(socket::timeout_error_msg(
Expand All @@ -3065,7 +3063,7 @@ mod _ssl {
let (needs, state) = stream
.get_ref()
.expect("write called in bio mode; should only be called in socket mode")
.socket_needs(&err, &timeout, vm);
.socket_needs(&err, &timeout, vm)?;
match state {
SelectRet::TimedOut => {
return Err(socket::timeout_error_msg(
Expand Down Expand Up @@ -3236,7 +3234,7 @@ mod _ssl {
let (needs, state) = stream
.get_ref()
.expect("read called in bio mode; should only be called in socket mode")
.socket_needs(&err, &timeout, vm);
.socket_needs(&err, &timeout, vm)?;
match state {
SelectRet::TimedOut => {
return Err(socket::timeout_error_msg(
Expand Down Expand Up @@ -4149,7 +4147,7 @@ mod bio {
use openssl_sys as sys;
use std::marker::PhantomData;

pub struct MemBioSlice<'a>(*mut sys::BIO, PhantomData<&'a [u8]>);
pub(super) struct MemBioSlice<'a>(*mut sys::BIO, PhantomData<&'a [u8]>);

impl Drop for MemBioSlice<'_> {
fn drop(&mut self) {
Expand All @@ -4160,7 +4158,7 @@ mod bio {
}

impl<'a> MemBioSlice<'a> {
pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
pub(super) fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
openssl::init();

assert!(buf.len() <= c_int::MAX as usize);
Expand All @@ -4172,7 +4170,7 @@ mod bio {
Ok(MemBioSlice(bio, PhantomData))
}

pub fn as_ptr(&self) -> *mut sys::BIO {
pub(super) fn as_ptr(&self) -> *mut sys::BIO {
self.0
}
}
Expand Down
8 changes: 8 additions & 0 deletions crates/stdlib/src/ssl/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ pub(crate) mod ssl_error {
)
}

#[cfg_attr(
all(feature = "ssl-openssl", not(feature = "ssl-rustls")),
expect(dead_code)
)]
pub(crate) fn create_ssl_zero_return_error(vm: &VirtualMachine) -> PyRef<PyOSError> {
vm.new_os_subtype_error(
PySSLZeroReturnError::class(&vm.ctx).to_owned(),
Expand All @@ -133,6 +137,10 @@ pub(crate) mod ssl_error {
)
}

#[cfg_attr(
all(feature = "ssl-openssl", not(feature = "ssl-rustls")),
expect(dead_code)
)]
pub(crate) fn create_ssl_syscall_error(
vm: &VirtualMachine,
msg: impl Into<String>,
Expand Down
Loading