Equivalent to OpenSSL's SSL_do_handshake() Performs TLS handshake by exchanging data with the peer until completion. This abstracts away the low-level rustls read_tls/write_tls loop. = SSL_do_handshake()
(
conn: &mut TlsConnection,
socket: &PySSLSocket,
vm: &VirtualMachine,
)
| 1442 | /// |
| 1443 | /// = SSL_do_handshake() |
| 1444 | pub(super) fn ssl_do_handshake( |
| 1445 | conn: &mut TlsConnection, |
| 1446 | socket: &PySSLSocket, |
| 1447 | vm: &VirtualMachine, |
| 1448 | ) -> SslResult<()> { |
| 1449 | // Check if handshake is already done |
| 1450 | if !conn.is_handshaking() { |
| 1451 | return Ok(()); |
| 1452 | } |
| 1453 | |
| 1454 | let is_bio = socket.is_bio_mode(); |
| 1455 | let is_server = matches!(conn, TlsConnection::Server(_)); |
| 1456 | let mut first_iteration = true; // Track if this is the first loop iteration |
| 1457 | let mut iteration_count = 0; |
| 1458 | |
| 1459 | loop { |
| 1460 | iteration_count += 1; |
| 1461 | let mut made_progress = false; |
| 1462 | |
| 1463 | // IMPORTANT: In BIO mode, force initial write even if wants_write() is false |
| 1464 | // rustls requires write_tls() to be called to generate ClientHello/ServerHello |
| 1465 | let force_initial_write = is_bio && first_iteration; |
| 1466 | |
| 1467 | // Write TLS handshake data to socket/BIO |
| 1468 | let write_progress = handshake_write_loop(conn, socket, force_initial_write, vm)?; |
| 1469 | made_progress |= write_progress; |
| 1470 | |
| 1471 | // Read TLS handshake data from socket/BIO |
| 1472 | let (read_progress, is_first_sni_read) = |
| 1473 | handshake_read_data(conn, socket, is_bio, is_server, vm)?; |
| 1474 | made_progress |= read_progress; |
| 1475 | |
| 1476 | // Process TLS packets (state machine) |
| 1477 | if let Err(e) = conn.process_new_packets() { |
| 1478 | // Send close_notify on error |
| 1479 | if !is_bio { |
| 1480 | conn.send_close_notify(); |
| 1481 | // Flush any pending TLS data before sending close_notify |
| 1482 | let _ = socket.flush_pending_tls_output(vm, None); |
| 1483 | // Actually send the close_notify alert using send_all_bytes |
| 1484 | // for proper partial send handling and retry logic |
| 1485 | if let Ok(alert_data) = ssl_write_tls_records(conn) |
| 1486 | && !alert_data.is_empty() |
| 1487 | { |
| 1488 | let _ = send_all_bytes(socket, alert_data, vm, None); |
| 1489 | } |
| 1490 | } |
| 1491 | |
| 1492 | // InvalidMessage during handshake means non-TLS data was received |
| 1493 | // before the handshake completed (e.g., HTTP request to TLS server) |
| 1494 | if matches!(e, rustls::Error::InvalidMessage(_)) { |
| 1495 | return Err(SslError::PreauthData); |
| 1496 | } |
| 1497 | |
| 1498 | // Certificate verification errors are already handled by from_rustls |
| 1499 | |
| 1500 | return Err(SslError::from_rustls(e)); |
| 1501 | } |
no test coverage detected