Loading src/event/iocp.cpp +36 −30 Original line number Diff line number Diff line Loading @@ -51,8 +51,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define BLOCKSIZE 16384 #endif // Set to 1 to enable debug logging - REBUILD CHECK 4 #define IOCP_DEBUG 1 // Set to 1 to enable debug logging #define IOCP_DEBUG 0 #if IOCP_DEBUG #define IOCP_LOG(x) std::cerr << x << std::endl Loading Loading @@ -495,9 +495,13 @@ namespace netplus { std::cerr << "[IOCP] Found connection, operation=" << (buf->operation == OP_READ ? "READ" : "WRITE") << " bytes=" << bytes << " InternalHigh=" << buf->overlapped.InternalHigh << std::endl; #endif // For READ completions: push data directly into the socket's buffer // This avoids race conditions with InternalHigh being overwritten if (buf->operation == OP_READ && bytes > 0) { // For READ completions: hold event_mutex for entire processing // This serializes all READ handling for this connection if (buf->operation == OP_READ) { std::lock_guard<std::mutex> lock(owner->event_mutex); // Push data into socket's buffer while holding lock if (bytes > 0) { owner->csock->pushReceivedData( reinterpret_cast<const uint8_t*>(buf->data.buf), bytes Loading Loading @@ -525,7 +529,6 @@ namespace netplus { continue; } if (buf->operation == OP_READ) { if (bytes == 0) { // Connection closed by peer owner->ReadPending.store(false); Loading Loading @@ -578,7 +581,7 @@ handshake_continue: #if IOCP_DEBUG std::cerr << "[IOCP] Starting handshake_after_accept (fd=" << owner->csock->fd() << ")..." << std::endl; #endif std::lock_guard<std::mutex> hsLock(owner->event_mutex); // event_mutex already held try { // Process handshake from internal buffer owner->csock->handshake_after_accept(); Loading Loading @@ -813,8 +816,10 @@ handshake_continue: continue; } // NO MUTEX HERE - avoid deadlock with flush_out // WritePending atomic protects against concurrent try_post_send calls // Hold event_mutex for application data handling to protect SSL state // The handshake path above already holds the mutex { std::lock_guard<std::mutex> lock(owner->event_mutex); owner->WritePending.store(false); Loading Loading @@ -876,6 +881,7 @@ handshake_continue: } catch (...) { try_cleanup_con(ev, st, owner, cs, tid); } } // end lock_guard scope // send buffer is internal - no delete // if (buf) delete buf; Loading src/socket.h +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <array> #include <iostream> #include <atomic> #include <mutex> #include <string> #include <memory> #include <deque> Loading Loading @@ -367,6 +368,7 @@ namespace netplus { bool hasBufferedData() const { return !_rx_tcp_buf.empty(); } // Push received data directly into the buffer (called by IOCP) // Caller must hold con::event_mutex void pushReceivedData(const uint8_t* data, size_t len) override { _rx_tcp_buf.insert(_rx_tcp_buf.end(), data, data + len); } Loading src/ssl.cpp +70 −40 Original line number Diff line number Diff line Loading @@ -360,67 +360,47 @@ netplus::ssl::ssl(std::shared_ptr<netplus::x509cert> cert, int sock) : std::vector<uint8_t> netplus::ssl::readTlsRecordAsync() { // Caller must hold con::event_mutex to protect _rx_tcp_buf #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: entry _rx_tcp_buf.size()=" << _rx_tcp_buf.size() << std::endl; // ---------------------------------------------------------------- // Helper lambda: try to read more data from underlying TCP socket // On IOCP (Windows): data is pushed via pushReceivedData(), tcp::recvData throws Note // On epoll (Linux): tcp::recvData() calls ::recv() synchronously // ---------------------------------------------------------------- auto tryReadMore = [this]() -> bool { try { buffer buf(16384); size_t got = tcp::recvData(buf, 0); if (got > 0) { _rx_tcp_buf.insert(_rx_tcp_buf.end(), (uint8_t*)buf.data.buf, (uint8_t*)buf.data.buf + got); std::cerr << "[SSL] readTlsRecordAsync: tcp::recvData got " << got << " bytes, _rx_tcp_buf now " << _rx_tcp_buf.size() << std::endl; return true; } } catch (const NetException& e) { // Note = would block or no data (IOCP case) // Error = connection closed if (e.getErrorType() != NetException::Note) { throw; // re-throw real errors } } return false; }; #endif // ---------------------------------------------------------------- // 1) ensure we have at least TLS record header (5 bytes) // On Windows/IOCP: data arrives via pushReceivedData(), just check buffer // On Linux/epoll: would need to call tcp::recvData() - but that's handled // by the event loop before calling ssl::recvData() // ---------------------------------------------------------------- while (_rx_tcp_buf.size() < 5) { if (_rx_tcp_buf.size() < 5) { #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: need header, only have " << _rx_tcp_buf.size() << " bytes" << std::endl; if (!tryReadMore()) { #endif NetException n; n[NetException::Note] << "ssl: record incomplete (need header)"; throw n; } } // ------------------------------------------------------------ // 2) parse record length // ------------------------------------------------------------ uint16_t recLen = (uint16_t(_rx_tcp_buf[3]) << 8) | uint16_t(_rx_tcp_buf[4]); size_t total = 5 + recLen; #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: header found, recLen=" << recLen << " total=" << total << " type=0x" << std::hex << (int)_rx_tcp_buf[0] << std::dec << std::endl; #endif // ------------------------------------------------------------ // 3) ensure full record is present // ------------------------------------------------------------ while (_rx_tcp_buf.size() < total) { if (_rx_tcp_buf.size() < total) { #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: need " << total << " bytes, only have " << _rx_tcp_buf.size() << std::endl; if (!tryReadMore()) { #endif NetException n; n[NetException::Note] << "ssl: record incomplete"; throw n; } } // ------------------------------------------------------------ // 4) extract full record Loading Loading @@ -1239,14 +1219,20 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() for (;;) { // Loop to skip CCS records std::vector<uint8_t> rec = readTlsRecordAsync(); if (rec.empty()) { #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: readTlsRecordAsync returned empty" << std::endl; #endif return {}; } #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: got record, size=" << rec.size() << std::endl; #endif if (rec.size() < 5) { #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: record too short (" << rec.size() << " < 5)" << std::endl; #endif throwSSL(NetException::Error, "TLS1.3 record too short"); } Loading @@ -1254,13 +1240,17 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() uint16_t ver = (uint16_t(rec[1]) << 8) | rec[2]; uint16_t rlen = (uint16_t(rec[3]) << 8) | rec[4]; #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: outer_type=0x" << std::hex << (int)outer_type << std::dec << " ver=0x" << std::hex << ver << std::dec << " rlen=" << rlen << std::endl; #endif // ✅ Skip CCS records (middlebox compatibility in TLS 1.3) if (outer_type == 0x14) { if (rec.size() == 6 && rec[5] == 0x01) { #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: skipping CCS record" << std::endl; #endif continue; // ignore CCS, read next record } } Loading Loading @@ -1326,12 +1316,15 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() uint8_t inner_type = inner[end - 1]; #if SSL_DEBUG std::cerr << "[TLS] record decrypted: size=" << inner.size() << " stripped=" << end << " type=0x" << std::hex << (int)inner_type << std::dec << std::endl; std::cerr.flush(); #endif // ✅ If alert, allow it through for debugging (client may be rejecting handshake) if (inner_type == 0x15) { // Alert: level (byte 0) + description (byte 1, in the plaintext) #if SSL_DEBUG std::cerr << "[TLS] ALERT RECEIVED from client: "; if (end >= 2) { uint8_t level = inner[0]; Loading @@ -1340,6 +1333,7 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() } std::cerr << std::endl; std::cerr.flush(); #endif // Continue anyway for now to see what happens } Loading Loading @@ -1593,8 +1587,10 @@ void netplus::ssl::_tls13_send_finished(bool handshake_keys){ void netplus::ssl::handshake_after_accept(){ // Note: Caller (IOCP layer) must hold con::event_mutex to prevent concurrent access #if SSL_DEBUG std::cerr << "[SSL] ===== handshake_after_accept ENTER state=" << (int)_hs_state << std::endl; std::cerr.flush(); #endif auto throwSSL = [&](int type, const std::string& msg) { netplus::NetException e; Loading Loading @@ -1667,23 +1663,31 @@ void netplus::ssl::handshake_after_accept(){ // Run as far as possible in one call, until IO would block (Note thrown) try { for (;;) { #if SSL_DEBUG std::cerr << "[SSL] handshake_after_accept loop: state=" << (int)_hs_state << std::endl; std::cerr.flush(); #endif switch (_hs_state) { case HsState::READ_CLIENT_HELLO: { #if SSL_DEBUG std::cerr << "[SSL] Processing READ_CLIENT_HELLO state" << std::endl; std::cerr.flush(); #endif std::vector<uint8_t> ch = _fetchNextHandshakePlain(); if (ch.empty()) { #if SSL_DEBUG std::cerr << "[SSL] READ_CLIENT_HELLO: fetchNextHandshakePlain returned empty - need more data" << std::endl; std::cerr.flush(); #endif return; } #if SSL_DEBUG std::cerr << "[SSL] READ_CLIENT_HELLO: got " << ch.size() << " bytes of handshake message" << std::endl; std::cerr.flush(); #endif if (ch.size() < 4) throwSSL(NetException::Error, "ClientHello too short"); if (ch[0] != 0x01) throwSSL(NetException::Error, "Expected ClientHello"); Loading Loading @@ -2164,12 +2168,16 @@ void netplus::ssl::handshake_after_accept(){ // WAIT_CCS // ============================================================ case HsState::WAIT_CCS: { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: waiting for ChangeCipherSpec record" << std::endl; std::cerr.flush(); #endif if (_ccs_received) { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: CCS already received, transitioning to WAIT_FIN" << std::endl; std::cerr.flush(); #endif _recv_seq = 0; _hs_state = HsState::WAIT_FIN; continue; // direkt WAIT_FIN probieren Loading @@ -2180,22 +2188,28 @@ void netplus::ssl::handshake_after_accept(){ rec = readTlsRecordAsync(); } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: No data yet, returning" << std::endl; std::cerr.flush(); #endif return; } TLSDBG("readTlsRecordAsync ERROR"); throw; } #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: Got record, size=" << rec.size() << " type=" << (int)rec[0] << std::endl; std::cerr.flush(); #endif if (rec.size() != 6 || rec[0] != 0x14 || rec[5] != 0x01) throwSSL(NetException::Error, "Expected CCS"); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: CCS received correctly, transitioning to WAIT_FIN" << std::endl; std::cerr.flush(); #endif _ccs_received = true; // bleibt true bis WAIT_FIN erfolgreich war _recv_seq = 0; Loading Loading @@ -2297,17 +2311,23 @@ void netplus::ssl::handshake_after_accept(){ } case HsState::WAIT_CKE: { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: calling _fetchNextHandshakePlain()" << std::endl; std::cerr.flush(); #endif // ✅ Always read full handshake message (handles fragmentation) std::vector<uint8_t> msg = _fetchNextHandshakePlain(); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: _fetchNextHandshakePlain returned " << msg.size() << " bytes" << std::endl; std::cerr.flush(); #endif if (msg.empty()) { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: No data yet, returning" << std::endl; std::cerr.flush(); #endif return; // would block } Loading Loading @@ -2344,36 +2364,46 @@ void netplus::ssl::handshake_after_accept(){ " kBytes=" + std::to_string(kBytes)); } #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Decrypting RSA ciphertext (" << encLen << " bytes)..." << std::endl; std::cerr.flush(); #endif rsa::bigInt cipher = rsa::bigIntFromBytesBE(msg.data() + off, encLen); rsa::bigInt plainBI = _rsa.decrypt(cipher); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: RSA decryption successful" << std::endl; std::cerr.flush(); #endif std::vector<uint8_t> pkcs1 = rsa::bigIntToBytesBE(plainBI, kBytes); std::vector<uint8_t> preMaster = extractPreMasterFromPkcs1(pkcs1); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Extracted premaster (" << preMaster.size() << " bytes)" << std::endl; std::cerr.flush(); #endif // master_secret = PRF(PMS, "master secret", client_random || server_random) std::vector<uint8_t> msSeed = _clientRandom; msSeed.insert(msSeed.end(), _serverRandom.begin(), _serverRandom.end()); _masterSecret = _prf(preMaster, "master secret", msSeed, 48); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Master secret derived" << std::endl; std::cerr.flush(); #endif // key_block = PRF(master, "key expansion", server_random || client_random) std::vector<uint8_t> kbSeed = _serverRandom; kbSeed.insert(kbSeed.end(), _clientRandom.begin(), _clientRandom.end()); std::vector<uint8_t> keyBlock = _prf(_masterSecret, "key expansion", kbSeed, 72); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Key block derived" << std::endl; std::cerr.flush(); #endif size_t k = 0; _client_mac_key.assign(keyBlock.begin() + k, keyBlock.begin() + k + 20); k += 20; Loading Loading
src/event/iocp.cpp +36 −30 Original line number Diff line number Diff line Loading @@ -51,8 +51,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define BLOCKSIZE 16384 #endif // Set to 1 to enable debug logging - REBUILD CHECK 4 #define IOCP_DEBUG 1 // Set to 1 to enable debug logging #define IOCP_DEBUG 0 #if IOCP_DEBUG #define IOCP_LOG(x) std::cerr << x << std::endl Loading Loading @@ -495,9 +495,13 @@ namespace netplus { std::cerr << "[IOCP] Found connection, operation=" << (buf->operation == OP_READ ? "READ" : "WRITE") << " bytes=" << bytes << " InternalHigh=" << buf->overlapped.InternalHigh << std::endl; #endif // For READ completions: push data directly into the socket's buffer // This avoids race conditions with InternalHigh being overwritten if (buf->operation == OP_READ && bytes > 0) { // For READ completions: hold event_mutex for entire processing // This serializes all READ handling for this connection if (buf->operation == OP_READ) { std::lock_guard<std::mutex> lock(owner->event_mutex); // Push data into socket's buffer while holding lock if (bytes > 0) { owner->csock->pushReceivedData( reinterpret_cast<const uint8_t*>(buf->data.buf), bytes Loading Loading @@ -525,7 +529,6 @@ namespace netplus { continue; } if (buf->operation == OP_READ) { if (bytes == 0) { // Connection closed by peer owner->ReadPending.store(false); Loading Loading @@ -578,7 +581,7 @@ handshake_continue: #if IOCP_DEBUG std::cerr << "[IOCP] Starting handshake_after_accept (fd=" << owner->csock->fd() << ")..." << std::endl; #endif std::lock_guard<std::mutex> hsLock(owner->event_mutex); // event_mutex already held try { // Process handshake from internal buffer owner->csock->handshake_after_accept(); Loading Loading @@ -813,8 +816,10 @@ handshake_continue: continue; } // NO MUTEX HERE - avoid deadlock with flush_out // WritePending atomic protects against concurrent try_post_send calls // Hold event_mutex for application data handling to protect SSL state // The handshake path above already holds the mutex { std::lock_guard<std::mutex> lock(owner->event_mutex); owner->WritePending.store(false); Loading Loading @@ -876,6 +881,7 @@ handshake_continue: } catch (...) { try_cleanup_con(ev, st, owner, cs, tid); } } // end lock_guard scope // send buffer is internal - no delete // if (buf) delete buf; Loading
src/socket.h +2 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <array> #include <iostream> #include <atomic> #include <mutex> #include <string> #include <memory> #include <deque> Loading Loading @@ -367,6 +368,7 @@ namespace netplus { bool hasBufferedData() const { return !_rx_tcp_buf.empty(); } // Push received data directly into the buffer (called by IOCP) // Caller must hold con::event_mutex void pushReceivedData(const uint8_t* data, size_t len) override { _rx_tcp_buf.insert(_rx_tcp_buf.end(), data, data + len); } Loading
src/ssl.cpp +70 −40 Original line number Diff line number Diff line Loading @@ -360,67 +360,47 @@ netplus::ssl::ssl(std::shared_ptr<netplus::x509cert> cert, int sock) : std::vector<uint8_t> netplus::ssl::readTlsRecordAsync() { // Caller must hold con::event_mutex to protect _rx_tcp_buf #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: entry _rx_tcp_buf.size()=" << _rx_tcp_buf.size() << std::endl; // ---------------------------------------------------------------- // Helper lambda: try to read more data from underlying TCP socket // On IOCP (Windows): data is pushed via pushReceivedData(), tcp::recvData throws Note // On epoll (Linux): tcp::recvData() calls ::recv() synchronously // ---------------------------------------------------------------- auto tryReadMore = [this]() -> bool { try { buffer buf(16384); size_t got = tcp::recvData(buf, 0); if (got > 0) { _rx_tcp_buf.insert(_rx_tcp_buf.end(), (uint8_t*)buf.data.buf, (uint8_t*)buf.data.buf + got); std::cerr << "[SSL] readTlsRecordAsync: tcp::recvData got " << got << " bytes, _rx_tcp_buf now " << _rx_tcp_buf.size() << std::endl; return true; } } catch (const NetException& e) { // Note = would block or no data (IOCP case) // Error = connection closed if (e.getErrorType() != NetException::Note) { throw; // re-throw real errors } } return false; }; #endif // ---------------------------------------------------------------- // 1) ensure we have at least TLS record header (5 bytes) // On Windows/IOCP: data arrives via pushReceivedData(), just check buffer // On Linux/epoll: would need to call tcp::recvData() - but that's handled // by the event loop before calling ssl::recvData() // ---------------------------------------------------------------- while (_rx_tcp_buf.size() < 5) { if (_rx_tcp_buf.size() < 5) { #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: need header, only have " << _rx_tcp_buf.size() << " bytes" << std::endl; if (!tryReadMore()) { #endif NetException n; n[NetException::Note] << "ssl: record incomplete (need header)"; throw n; } } // ------------------------------------------------------------ // 2) parse record length // ------------------------------------------------------------ uint16_t recLen = (uint16_t(_rx_tcp_buf[3]) << 8) | uint16_t(_rx_tcp_buf[4]); size_t total = 5 + recLen; #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: header found, recLen=" << recLen << " total=" << total << " type=0x" << std::hex << (int)_rx_tcp_buf[0] << std::dec << std::endl; #endif // ------------------------------------------------------------ // 3) ensure full record is present // ------------------------------------------------------------ while (_rx_tcp_buf.size() < total) { if (_rx_tcp_buf.size() < total) { #if SSL_DEBUG std::cerr << "[SSL] readTlsRecordAsync: need " << total << " bytes, only have " << _rx_tcp_buf.size() << std::endl; if (!tryReadMore()) { #endif NetException n; n[NetException::Note] << "ssl: record incomplete"; throw n; } } // ------------------------------------------------------------ // 4) extract full record Loading Loading @@ -1239,14 +1219,20 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() for (;;) { // Loop to skip CCS records std::vector<uint8_t> rec = readTlsRecordAsync(); if (rec.empty()) { #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: readTlsRecordAsync returned empty" << std::endl; #endif return {}; } #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: got record, size=" << rec.size() << std::endl; #endif if (rec.size() < 5) { #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: record too short (" << rec.size() << " < 5)" << std::endl; #endif throwSSL(NetException::Error, "TLS1.3 record too short"); } Loading @@ -1254,13 +1240,17 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() uint16_t ver = (uint16_t(rec[1]) << 8) | rec[2]; uint16_t rlen = (uint16_t(rec[3]) << 8) | rec[4]; #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: outer_type=0x" << std::hex << (int)outer_type << std::dec << " ver=0x" << std::hex << ver << std::dec << " rlen=" << rlen << std::endl; #endif // ✅ Skip CCS records (middlebox compatibility in TLS 1.3) if (outer_type == 0x14) { if (rec.size() == 6 && rec[5] == 0x01) { #if SSL_DEBUG std::cerr << "[SSL] _tls13_read_record_handshake: skipping CCS record" << std::endl; #endif continue; // ignore CCS, read next record } } Loading Loading @@ -1326,12 +1316,15 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() uint8_t inner_type = inner[end - 1]; #if SSL_DEBUG std::cerr << "[TLS] record decrypted: size=" << inner.size() << " stripped=" << end << " type=0x" << std::hex << (int)inner_type << std::dec << std::endl; std::cerr.flush(); #endif // ✅ If alert, allow it through for debugging (client may be rejecting handshake) if (inner_type == 0x15) { // Alert: level (byte 0) + description (byte 1, in the plaintext) #if SSL_DEBUG std::cerr << "[TLS] ALERT RECEIVED from client: "; if (end >= 2) { uint8_t level = inner[0]; Loading @@ -1340,6 +1333,7 @@ std::vector<uint8_t> netplus::ssl::_tls13_read_record_handshake() } std::cerr << std::endl; std::cerr.flush(); #endif // Continue anyway for now to see what happens } Loading Loading @@ -1593,8 +1587,10 @@ void netplus::ssl::_tls13_send_finished(bool handshake_keys){ void netplus::ssl::handshake_after_accept(){ // Note: Caller (IOCP layer) must hold con::event_mutex to prevent concurrent access #if SSL_DEBUG std::cerr << "[SSL] ===== handshake_after_accept ENTER state=" << (int)_hs_state << std::endl; std::cerr.flush(); #endif auto throwSSL = [&](int type, const std::string& msg) { netplus::NetException e; Loading Loading @@ -1667,23 +1663,31 @@ void netplus::ssl::handshake_after_accept(){ // Run as far as possible in one call, until IO would block (Note thrown) try { for (;;) { #if SSL_DEBUG std::cerr << "[SSL] handshake_after_accept loop: state=" << (int)_hs_state << std::endl; std::cerr.flush(); #endif switch (_hs_state) { case HsState::READ_CLIENT_HELLO: { #if SSL_DEBUG std::cerr << "[SSL] Processing READ_CLIENT_HELLO state" << std::endl; std::cerr.flush(); #endif std::vector<uint8_t> ch = _fetchNextHandshakePlain(); if (ch.empty()) { #if SSL_DEBUG std::cerr << "[SSL] READ_CLIENT_HELLO: fetchNextHandshakePlain returned empty - need more data" << std::endl; std::cerr.flush(); #endif return; } #if SSL_DEBUG std::cerr << "[SSL] READ_CLIENT_HELLO: got " << ch.size() << " bytes of handshake message" << std::endl; std::cerr.flush(); #endif if (ch.size() < 4) throwSSL(NetException::Error, "ClientHello too short"); if (ch[0] != 0x01) throwSSL(NetException::Error, "Expected ClientHello"); Loading Loading @@ -2164,12 +2168,16 @@ void netplus::ssl::handshake_after_accept(){ // WAIT_CCS // ============================================================ case HsState::WAIT_CCS: { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: waiting for ChangeCipherSpec record" << std::endl; std::cerr.flush(); #endif if (_ccs_received) { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: CCS already received, transitioning to WAIT_FIN" << std::endl; std::cerr.flush(); #endif _recv_seq = 0; _hs_state = HsState::WAIT_FIN; continue; // direkt WAIT_FIN probieren Loading @@ -2180,22 +2188,28 @@ void netplus::ssl::handshake_after_accept(){ rec = readTlsRecordAsync(); } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: No data yet, returning" << std::endl; std::cerr.flush(); #endif return; } TLSDBG("readTlsRecordAsync ERROR"); throw; } #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: Got record, size=" << rec.size() << " type=" << (int)rec[0] << std::endl; std::cerr.flush(); #endif if (rec.size() != 6 || rec[0] != 0x14 || rec[5] != 0x01) throwSSL(NetException::Error, "Expected CCS"); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CCS: CCS received correctly, transitioning to WAIT_FIN" << std::endl; std::cerr.flush(); #endif _ccs_received = true; // bleibt true bis WAIT_FIN erfolgreich war _recv_seq = 0; Loading Loading @@ -2297,17 +2311,23 @@ void netplus::ssl::handshake_after_accept(){ } case HsState::WAIT_CKE: { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: calling _fetchNextHandshakePlain()" << std::endl; std::cerr.flush(); #endif // ✅ Always read full handshake message (handles fragmentation) std::vector<uint8_t> msg = _fetchNextHandshakePlain(); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: _fetchNextHandshakePlain returned " << msg.size() << " bytes" << std::endl; std::cerr.flush(); #endif if (msg.empty()) { #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: No data yet, returning" << std::endl; std::cerr.flush(); #endif return; // would block } Loading Loading @@ -2344,36 +2364,46 @@ void netplus::ssl::handshake_after_accept(){ " kBytes=" + std::to_string(kBytes)); } #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Decrypting RSA ciphertext (" << encLen << " bytes)..." << std::endl; std::cerr.flush(); #endif rsa::bigInt cipher = rsa::bigIntFromBytesBE(msg.data() + off, encLen); rsa::bigInt plainBI = _rsa.decrypt(cipher); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: RSA decryption successful" << std::endl; std::cerr.flush(); #endif std::vector<uint8_t> pkcs1 = rsa::bigIntToBytesBE(plainBI, kBytes); std::vector<uint8_t> preMaster = extractPreMasterFromPkcs1(pkcs1); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Extracted premaster (" << preMaster.size() << " bytes)" << std::endl; std::cerr.flush(); #endif // master_secret = PRF(PMS, "master secret", client_random || server_random) std::vector<uint8_t> msSeed = _clientRandom; msSeed.insert(msSeed.end(), _serverRandom.begin(), _serverRandom.end()); _masterSecret = _prf(preMaster, "master secret", msSeed, 48); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Master secret derived" << std::endl; std::cerr.flush(); #endif // key_block = PRF(master, "key expansion", server_random || client_random) std::vector<uint8_t> kbSeed = _serverRandom; kbSeed.insert(kbSeed.end(), _clientRandom.begin(), _clientRandom.end()); std::vector<uint8_t> keyBlock = _prf(_masterSecret, "key expansion", kbSeed, 72); #if SSL_DEBUG std::cerr << "[SSL] WAIT_CKE: Key block derived" << std::endl; std::cerr.flush(); #endif size_t k = 0; _client_mac_key.assign(keyBlock.begin() + k, keyBlock.begin() + k + 20); k += 20; Loading