Skip to content

Commit b5d48e2

Browse files
committed
more ssl impl
1 parent fda9cee commit b5d48e2

File tree

12 files changed

+6360
-129
lines changed

12 files changed

+6360
-129
lines changed

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_httplib.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,8 +1801,6 @@ def test_local_good_hostname(self):
18011801
self.addCleanup(resp.close)
18021802
self.assertEqual(resp.status, 404)
18031803

1804-
# TODO: RUSTPYTHON
1805-
@unittest.expectedFailure
18061804
def test_local_bad_hostname(self):
18071805
# The (valid) cert doesn't validate the HTTP hostname
18081806
import ssl

Lib/test/test_ssl.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,6 @@ def test_refcycle(self):
586586
del ss
587587
self.assertEqual(wr(), None)
588588

589-
@unittest.expectedFailure # TODO: RUSTPYTHON
590589
def test_wrapped_unconnected(self):
591590
# Methods on an unconnected SSLSocket propagate the original
592591
# OSError raise by the underlying socket object.
@@ -1400,7 +1399,6 @@ def test_load_dh_params(self):
14001399
with self.assertRaises(ssl.SSLError) as cm:
14011400
ctx.load_dh_params(CERTFILE)
14021401

1403-
@unittest.expectedFailure # TODO: RUSTPYTHON
14041402
def test_session_stats(self):
14051403
for proto in {ssl.PROTOCOL_TLS_CLIENT, ssl.PROTOCOL_TLS_SERVER}:
14061404
ctx = ssl.SSLContext(proto)
@@ -1461,7 +1459,6 @@ def dummycallback(sock, servername, ctx, cycle=ctx):
14611459
gc.collect()
14621460
self.assertIs(wr(), None)
14631461

1464-
@unittest.expectedFailure # TODO: RUSTPYTHON
14651462
def test_cert_store_stats(self):
14661463
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
14671464
self.assertEqual(ctx.cert_store_stats(),
@@ -1521,7 +1518,6 @@ def test_load_default_certs(self):
15211518
self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH')
15221519

15231520
@unittest.skipIf(sys.platform == "win32", "not-Windows specific")
1524-
@unittest.expectedFailure # TODO: RUSTPYTHON
15251521
def test_load_default_certs_env(self):
15261522
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
15271523
with os_helper.EnvironmentVarGuard() as env:
@@ -1673,7 +1669,6 @@ def test_context_client_server(self):
16731669
self.assertFalse(ctx.check_hostname)
16741670
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
16751671

1676-
@unittest.expectedFailure # TODO: RUSTPYTHON
16771672
def test_context_custom_class(self):
16781673
class MySSLSocket(ssl.SSLSocket):
16791674
pass
@@ -1690,7 +1685,6 @@ class MySSLObject(ssl.SSLObject):
16901685
obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO(), server_side=True)
16911686
self.assertIsInstance(obj, MySSLObject)
16921687

1693-
@unittest.expectedFailure # TODO: RUSTPYTHON
16941688
def test_num_tickest(self):
16951689
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
16961690
self.assertEqual(ctx.num_tickets, 2)
@@ -1721,7 +1715,6 @@ def test_str(self):
17211715
self.assertEqual(str(e), "foo")
17221716
self.assertEqual(e.errno, 1)
17231717

1724-
@unittest.expectedFailure # TODO: RUSTPYTHON
17251718
def test_lib_reason(self):
17261719
# Test the library and reason attributes
17271720
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -1899,7 +1892,6 @@ def setUp(self):
18991892
self.enterContext(server)
19001893
self.server_addr = (HOST, server.port)
19011894

1902-
@unittest.expectedFailure # TODO: RUSTPYTHON
19031895
def test_connect(self):
19041896
with test_wrap_socket(socket.socket(socket.AF_INET),
19051897
cert_reqs=ssl.CERT_NONE) as s:
@@ -1966,7 +1958,6 @@ def test_non_blocking_connect_ex(self):
19661958
# SSL established
19671959
self.assertTrue(s.getpeercert())
19681960

1969-
@unittest.expectedFailure # TODO: RUSTPYTHON
19701961
def test_connect_with_context(self):
19711962
# Same as test_connect, but with a separately created context
19721963
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -2093,7 +2084,6 @@ def test_non_blocking_handshake(self):
20932084
def test_get_server_certificate(self):
20942085
_test_get_server_certificate(self, *self.server_addr, cert=SIGNING_CA)
20952086

2096-
@unittest.expectedFailure # TODO: RUSTPYTHON
20972087
def test_get_server_certificate_sni(self):
20982088
host, port = self.server_addr
20992089
server_names = []
@@ -2120,7 +2110,6 @@ def test_get_server_certificate_fail(self):
21202110
# independent test method
21212111
_test_get_server_certificate_fail(self, *self.server_addr)
21222112

2123-
@unittest.expectedFailure # TODO: RUSTPYTHON
21242113
def test_get_server_certificate_timeout(self):
21252114
def servername_cb(ssl_sock, server_name, initial_context):
21262115
time.sleep(0.2)
@@ -2156,7 +2145,6 @@ def test_get_ca_certs_capath(self):
21562145
self.assertTrue(cert)
21572146
self.assertEqual(len(ctx.get_ca_certs()), 1)
21582147

2159-
@unittest.expectedFailure # TODO: RUSTPYTHON
21602148
def test_context_setget(self):
21612149
# Check that the context of a connected socket can be replaced.
21622150
ctx1 = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -3041,7 +3029,6 @@ def test_crl_check(self):
30413029
cert = s.getpeercert()
30423030
self.assertTrue(cert, "Can't get peer certificate.")
30433031

3044-
@unittest.expectedFailure # TODO: RUSTPYTHON
30453032
def test_check_hostname(self):
30463033
if support.verbose:
30473034
sys.stdout.write("\n")
@@ -3182,7 +3169,6 @@ def test_dual_rsa_ecc(self):
31823169
cipher = s.cipher()[0].split('-')
31833170
self.assertTrue(cipher[:2], ('ECDHE', 'ECDSA'))
31843171

3185-
@unittest.expectedFailure # TODO: RUSTPYTHON
31863172
def test_check_hostname_idn(self, warnings_filters=True):
31873173
if support.verbose:
31883174
sys.stdout.write("\n")
@@ -3370,7 +3356,6 @@ def connector():
33703356
finally:
33713357
t.join()
33723358

3373-
@unittest.expectedFailure # TODO: RUSTPYTHON
33743359
def test_ssl_cert_verify_error(self):
33753360
if support.verbose:
33763361
sys.stdout.write("\n")
@@ -3477,7 +3462,6 @@ def test_protocol_tlsv1_1(self):
34773462
try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False)
34783463

34793464
@requires_tls_version('TLSv1_2')
3480-
@unittest.expectedFailure # TODO: RUSTPYTHON
34813465
def test_protocol_tlsv1_2(self):
34823466
"""Connecting to a TLSv1.2 server with various client options.
34833467
Testing against older TLS versions."""
@@ -3924,7 +3908,6 @@ def test_do_handshake_enotconn(self):
39243908
sock.do_handshake()
39253909
self.assertEqual(cm.exception.errno, errno.ENOTCONN)
39263910

3927-
@unittest.expectedFailure # TODO: RUSTPYTHON
39283911
def test_no_shared_ciphers(self):
39293912
client_context, server_context, hostname = testing_context()
39303913
# OpenSSL enables all TLS 1.3 ciphers, enforce TLS 1.2 for test
@@ -4156,7 +4139,6 @@ def test_no_legacy_server_connect(self):
41564139
chatty=True, connectionchatty=True,
41574140
sni_name=hostname)
41584141

4159-
@unittest.expectedFailure # TODO: RUSTPYTHON
41604142
def test_dh_params(self):
41614143
# Check we can get a connection with ephemeral finite-field
41624144
# Diffie-Hellman (if supported).
@@ -4755,7 +4737,6 @@ def test_pha_optional(self):
47554737
s.write(b'HASCERT')
47564738
self.assertEqual(s.recv(1024), b'TRUE\n')
47574739

4758-
@unittest.expectedFailure # TODO: RUSTPYTHON
47594740
def test_pha_optional_nocert(self):
47604741
if support.verbose:
47614742
sys.stdout.write("\n")
@@ -4795,7 +4776,6 @@ def test_pha_no_pha_client(self):
47954776
s.write(b'PHA')
47964777
self.assertIn(b'extension not received', s.recv(1024))
47974778

4798-
@unittest.expectedFailure # TODO: RUSTPYTHON
47994779
def test_pha_no_pha_server(self):
48004780
# server doesn't have PHA enabled, cert is requested in handshake
48014781
client_context, server_context, hostname = testing_context()
@@ -4816,7 +4796,6 @@ def test_pha_no_pha_server(self):
48164796
s.write(b'HASCERT')
48174797
self.assertEqual(s.recv(1024), b'TRUE\n')
48184798

4819-
@unittest.expectedFailure # TODO: RUSTPYTHON
48204799
def test_pha_not_tls13(self):
48214800
# TLS 1.2
48224801
client_context, server_context, hostname = testing_context()

Lib/test/test_urllib2_localnet.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,8 +602,6 @@ def test_https_with_cadefault(self):
602602
self.urlopen("https://localhost:%s/bizarre" % handler.port,
603603
cadefault=True)
604604

605-
# TODO: RUSTPYTHON
606-
@unittest.expectedFailure
607605
@unittest.skipIf(os.name == "nt", "TODO: RUSTPYTHON, ValueError: illegal environment variable name")
608606
def test_https_sni(self):
609607
if ssl is None:

common/src/fileutils.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,99 @@ pub mod windows {
438438
}
439439
}
440440
}
441+
442+
// _Py_fopen_obj in cpython (Python/fileutils.c:1757-1835)
443+
// Open a file using std::fs::File and convert to FILE*
444+
// Automatically handles path encoding and EINTR retries
445+
pub fn fopen(path: &std::path::Path, mode: &str) -> std::io::Result<*mut libc::FILE> {
446+
use std::ffi::CString;
447+
use std::fs::File;
448+
449+
// Currently only supports read mode
450+
// Can be extended to support "wb", "w+b", etc. if needed
451+
if mode != "rb" {
452+
return Err(std::io::Error::new(
453+
std::io::ErrorKind::InvalidInput,
454+
format!("unsupported mode: {}", mode),
455+
));
456+
}
457+
458+
// Open file using std::fs::File (handles path encoding and EINTR automatically)
459+
let file = File::open(path)?;
460+
461+
#[cfg(windows)]
462+
{
463+
use std::os::windows::io::IntoRawHandle;
464+
465+
// Declare Windows CRT functions
466+
unsafe extern "C" {
467+
fn _open_osfhandle(handle: isize, flags: libc::c_int) -> libc::c_int;
468+
fn _fdopen(fd: libc::c_int, mode: *const libc::c_char) -> *mut libc::FILE;
469+
}
470+
471+
// Convert File handle to CRT file descriptor
472+
let handle = file.into_raw_handle();
473+
let fd = unsafe { _open_osfhandle(handle as isize, libc::O_RDONLY) };
474+
if fd == -1 {
475+
return Err(std::io::Error::last_os_error());
476+
}
477+
478+
// Convert fd to FILE*
479+
let mode_cstr = CString::new(mode).unwrap();
480+
let fp = unsafe { _fdopen(fd, mode_cstr.as_ptr()) };
481+
if fp.is_null() {
482+
unsafe { libc::close(fd) };
483+
return Err(std::io::Error::last_os_error());
484+
}
485+
486+
// Set non-inheritable (Windows needs this explicitly)
487+
if let Err(e) = set_inheritable(fd, false) {
488+
unsafe { libc::fclose(fp) };
489+
return Err(e);
490+
}
491+
492+
Ok(fp)
493+
}
494+
495+
#[cfg(not(windows))]
496+
{
497+
use std::os::fd::IntoRawFd;
498+
499+
// Convert File to raw fd
500+
let fd = file.into_raw_fd();
501+
502+
// Convert fd to FILE*
503+
let mode_cstr = CString::new(mode).unwrap();
504+
let fp = unsafe { libc::fdopen(fd, mode_cstr.as_ptr()) };
505+
if fp.is_null() {
506+
unsafe { libc::close(fd) };
507+
return Err(std::io::Error::last_os_error());
508+
}
509+
510+
// Unix: O_CLOEXEC is already set by File::open, so non-inheritable is automatic
511+
Ok(fp)
512+
}
513+
}
514+
515+
// set_inheritable in cpython (Python/fileutils.c:1443-1570)
516+
// Set the inheritable flag of the specified file descriptor
517+
// Only used on Windows; Unix automatically sets O_CLOEXEC
518+
#[cfg(windows)]
519+
fn set_inheritable(fd: libc::c_int, inheritable: bool) -> std::io::Result<()> {
520+
use windows_sys::Win32::Foundation::{
521+
HANDLE, HANDLE_FLAG_INHERIT, INVALID_HANDLE_VALUE, SetHandleInformation,
522+
};
523+
524+
let handle = unsafe { libc::get_osfhandle(fd) };
525+
if handle == INVALID_HANDLE_VALUE as isize {
526+
return Err(std::io::Error::last_os_error());
527+
}
528+
529+
let flags = if inheritable { HANDLE_FLAG_INHERIT } else { 0 };
530+
let result = unsafe { SetHandleInformation(handle as HANDLE, HANDLE_FLAG_INHERIT, flags) };
531+
if result == 0 {
532+
return Err(std::io::Error::last_os_error());
533+
}
534+
535+
Ok(())
536+
}

0 commit comments

Comments
 (0)