Loading src/event/iocp.cpp +101 −78 Original line number Diff line number Diff line Loading @@ -93,19 +93,26 @@ namespace netplus { class EventWorker { public: /** * @brief Helper to initiate a raw overlapped read. * This fills the context buffer which recvDataWSA will later process. */ static void start_read(client* ctx) { con& c = *ctx->CurCon; // Point the WSA buffer to our persistent context buffer buffer buf(ctx->readCtx.buffer, BLOCKSIZE); if (c.csock->_Type == sockettype::TCP) { static_cast<tcp*>(c.csock.get())->recvDataWSA(buf, 0); } else if (c.csock->_Type == sockettype::UDP) { static_cast<udp*>(c.csock.get())->recvDataWSA(buf, 0); } else if (c.csock->_Type == sockettype::SSL) { // Note: For SSL, this initiates the raw TCP read into _rx_netbuf static_cast<ssl*>(c.csock.get())->recvDataWSA(buf, 0); } } /** * @brief Helper to initiate a raw overlapped write. */ static void start_write(client* ctx) { con& c = *ctx->CurCon; if (c.SendData.empty()) return; Loading @@ -115,28 +122,34 @@ namespace netplus { if (c.csock->_Type == sockettype::TCP) { static_cast<tcp*>(c.csock.get())->sendDataWSA(out, 0); } else if (c.csock->_Type == sockettype::UDP) { static_cast<udp*>(c.csock.get())->sendDataWSA(out, 0); } else if (c.csock->_Type == sockettype::SSL) { // This triggers SSL encryption then calls internal WSASend static_cast<ssl*>(c.csock.get())->sendDataWSA(out, 0); } } /** * @brief The core IOCP Worker Loop */ EventWorker(int tid, ULONG_PTR args, EventWorkerArgs* eargs) { while (event::Running) { DWORD dwBytesTransfered = 0; ULONG_PTR lpContext = 0; OVERLAPPED* pOverlapped = nullptr; // 1. Wait for completion notification from Windows Kernel BOOL bReturn = GetQueuedCompletionStatus(eargs->eviocp, &dwBytesTransfered, &lpContext, &pOverlapped, eargs->timeout); // Handle timeout (eargs->timeout) if (lpContext == 0 && GetLastError() == WAIT_TIMEOUT) continue; if (lpContext == 0) continue; client* pClientContext = reinterpret_cast<client*>(lpContext); IO_CONTEXT* pIoCtx = CONTAINING_RECORD(pOverlapped, IO_CONTEXT, overlapped); con& c = *pClientContext->CurCon; // 2. Handle Disconnection or Socket Errors if (!bReturn || (bReturn && dwBytesTransfered == 0)) { eargs->event->DisconnectEvent(c, tid, (ULONG_PTR)eargs->args); delete pClientContext; Loading @@ -149,23 +162,32 @@ namespace netplus { if (pIoCtx->operation == OP_READ) { if (c.csock->_Type == sockettype::SSL) { ssl* sslSocket = static_cast<ssl*>(c.csock.get()); sslSocket->_rx_netbuf.insert( sslSocket->_rx_netbuf.end(), pIoCtx->buffer, pIoCtx->buffer + dwBytesTransfered ); buffer plain(BLOCKSIZE); size_t decrypted = sslSocket->recvData(plain, 0); size_t decrypted = sslSocket->recvDataWSA(plain, 0); if (decrypted > 0) { c.RecvData.append(plain.data.buf, decrypted); eargs->event->RequestEvent(c, tid, (ULONG_PTR)eargs->args); } } else { // Standard TCP: Raw data is the application data c.RecvData.append(pIoCtx->buffer, dwBytesTransfered); eargs->event->RequestEvent(c, tid, (ULONG_PTR)eargs->args); } // Determine next step: Write if data pending, else keep reading if (!c.SendData.empty()) start_write(pClientContext); else start_read(pClientContext); } else if (pIoCtx->operation == OP_WRITE) { // FIXED: Use iterators for erasure to prevent compiler error // Update the SendData buffer based on successful wire transfer if (dwBytesTransfered > 0 && dwBytesTransfered <= c.SendData.size()) { c.SendData.erase(c.SendData.begin(), c.SendData.begin() + dwBytesTransfered); } Loading @@ -178,6 +200,7 @@ namespace netplus { else start_read(pClientContext); } } catch (NetException& e) { // Ignore "Note" level exceptions, but disconnect on Errors if (e.getErrorType() != NetException::Note) { eargs->event->DisconnectEvent(c, tid, (ULONG_PTR)eargs->args); delete pClientContext; Loading src/socket.h +1 −0 Original line number Diff line number Diff line Loading @@ -317,5 +317,6 @@ namespace netplus { friend class poll; friend class event; friend class EventWorker; }; }; src/ssl.cpp +49 −23 Original line number Diff line number Diff line Loading @@ -1801,39 +1801,65 @@ size_t netplus::ssl::sendDataWSA(buffer &data, int flags) { } size_t netplus::ssl::recvDataWSA(buffer &data, int flags) { // 1. Process ciphertext that was placed in _rx_netbuf by the IOCP // 1. Move raw ciphertext from the network buffer (filled by IOCP) to the record processor if (!_rx_netbuf.empty()) { // Append new network data to our decryption record buffer _recv_record.insert(_recv_record.end(), _rx_netbuf.begin(), _rx_netbuf.end()); _rx_netbuf.clear(); // Clear for next WSA request _rx_netbuf.clear(); // Clear to allow the next WSARecv to fill it } // 2. Attempt to decrypt a full TLS record if (_recv_record.size() >= 5) { // Minimum TLS Header size // 2. We need at least 5 bytes to read the TLS Record Header if (_recv_record.size() < 5) { return 0; } // 3. Parse TLS Record Header uint8_t type = _recv_record[0]; uint16_t version = (_recv_record[1] << 8) | _recv_record[2]; uint16_t length = (_recv_record[3] << 8) | _recv_record[4]; if (_recv_record.size() >= (size_t)(5 + length)) { // Validate TLS Major Version (Must be 3 for SSLv3/TLS 1.x) // This specifically prevents the "bad TLS major version" error if ((version >> 8) != 3) { NetException e; e[NetException::Error] << "ssl::recvDataWSA: bad TLS major version: " << (int)(version >> 8); throw e; } // 4. Check if the full encrypted record has arrived if (_recv_record.size() < (size_t)(5 + length)) { return 0; // Wait for more data from the next IOCP completion } // 5. Extract the fragment (ciphertext + MAC + Padding) std::vector<uint8_t> fragment( _recv_record.begin() + 5, _recv_record.begin() + 5 + length ); // 3. Decrypt the fragment using your CBC helper std::vector<uint8_t> plaintext = _decryptRecordCBC(type, version, fragment); // 6. Decryption Logic std::vector<uint8_t> plaintext; if (!_handshakeDone) { // During handshake, data is often unencrypted (ClientHello/ServerHello) // or uses specific handshake encryption states. plaintext = fragment; } else { // Decrypt the application data using the established AES/CBC state plaintext = _decryptRecordCBC(type, version, fragment); } // 4. Copy decrypted plaintext to the user-provided buffer // 7. Copy results to the application buffer size_t toCopy = (std::min)(data.size, plaintext.size()); if (toCopy > 0) { memcpy(data.data.buf, plaintext.data(), toCopy); } // Remove the processed record from the internal buffer // 8. Maintenance: Remove the processed record from the internal stream _recv_record.erase(_recv_record.begin(), _recv_record.begin() + 5 + length); // 9. If there's a protocol-level requirement (like a Handshake Finished), // it should be processed here before returning to the user. return toCopy; } } return 0; // No full record decrypted yet } #endif Loading
src/event/iocp.cpp +101 −78 Original line number Diff line number Diff line Loading @@ -93,19 +93,26 @@ namespace netplus { class EventWorker { public: /** * @brief Helper to initiate a raw overlapped read. * This fills the context buffer which recvDataWSA will later process. */ static void start_read(client* ctx) { con& c = *ctx->CurCon; // Point the WSA buffer to our persistent context buffer buffer buf(ctx->readCtx.buffer, BLOCKSIZE); if (c.csock->_Type == sockettype::TCP) { static_cast<tcp*>(c.csock.get())->recvDataWSA(buf, 0); } else if (c.csock->_Type == sockettype::UDP) { static_cast<udp*>(c.csock.get())->recvDataWSA(buf, 0); } else if (c.csock->_Type == sockettype::SSL) { // Note: For SSL, this initiates the raw TCP read into _rx_netbuf static_cast<ssl*>(c.csock.get())->recvDataWSA(buf, 0); } } /** * @brief Helper to initiate a raw overlapped write. */ static void start_write(client* ctx) { con& c = *ctx->CurCon; if (c.SendData.empty()) return; Loading @@ -115,28 +122,34 @@ namespace netplus { if (c.csock->_Type == sockettype::TCP) { static_cast<tcp*>(c.csock.get())->sendDataWSA(out, 0); } else if (c.csock->_Type == sockettype::UDP) { static_cast<udp*>(c.csock.get())->sendDataWSA(out, 0); } else if (c.csock->_Type == sockettype::SSL) { // This triggers SSL encryption then calls internal WSASend static_cast<ssl*>(c.csock.get())->sendDataWSA(out, 0); } } /** * @brief The core IOCP Worker Loop */ EventWorker(int tid, ULONG_PTR args, EventWorkerArgs* eargs) { while (event::Running) { DWORD dwBytesTransfered = 0; ULONG_PTR lpContext = 0; OVERLAPPED* pOverlapped = nullptr; // 1. Wait for completion notification from Windows Kernel BOOL bReturn = GetQueuedCompletionStatus(eargs->eviocp, &dwBytesTransfered, &lpContext, &pOverlapped, eargs->timeout); // Handle timeout (eargs->timeout) if (lpContext == 0 && GetLastError() == WAIT_TIMEOUT) continue; if (lpContext == 0) continue; client* pClientContext = reinterpret_cast<client*>(lpContext); IO_CONTEXT* pIoCtx = CONTAINING_RECORD(pOverlapped, IO_CONTEXT, overlapped); con& c = *pClientContext->CurCon; // 2. Handle Disconnection or Socket Errors if (!bReturn || (bReturn && dwBytesTransfered == 0)) { eargs->event->DisconnectEvent(c, tid, (ULONG_PTR)eargs->args); delete pClientContext; Loading @@ -149,23 +162,32 @@ namespace netplus { if (pIoCtx->operation == OP_READ) { if (c.csock->_Type == sockettype::SSL) { ssl* sslSocket = static_cast<ssl*>(c.csock.get()); sslSocket->_rx_netbuf.insert( sslSocket->_rx_netbuf.end(), pIoCtx->buffer, pIoCtx->buffer + dwBytesTransfered ); buffer plain(BLOCKSIZE); size_t decrypted = sslSocket->recvData(plain, 0); size_t decrypted = sslSocket->recvDataWSA(plain, 0); if (decrypted > 0) { c.RecvData.append(plain.data.buf, decrypted); eargs->event->RequestEvent(c, tid, (ULONG_PTR)eargs->args); } } else { // Standard TCP: Raw data is the application data c.RecvData.append(pIoCtx->buffer, dwBytesTransfered); eargs->event->RequestEvent(c, tid, (ULONG_PTR)eargs->args); } // Determine next step: Write if data pending, else keep reading if (!c.SendData.empty()) start_write(pClientContext); else start_read(pClientContext); } else if (pIoCtx->operation == OP_WRITE) { // FIXED: Use iterators for erasure to prevent compiler error // Update the SendData buffer based on successful wire transfer if (dwBytesTransfered > 0 && dwBytesTransfered <= c.SendData.size()) { c.SendData.erase(c.SendData.begin(), c.SendData.begin() + dwBytesTransfered); } Loading @@ -178,6 +200,7 @@ namespace netplus { else start_read(pClientContext); } } catch (NetException& e) { // Ignore "Note" level exceptions, but disconnect on Errors if (e.getErrorType() != NetException::Note) { eargs->event->DisconnectEvent(c, tid, (ULONG_PTR)eargs->args); delete pClientContext; Loading
src/socket.h +1 −0 Original line number Diff line number Diff line Loading @@ -317,5 +317,6 @@ namespace netplus { friend class poll; friend class event; friend class EventWorker; }; };
src/ssl.cpp +49 −23 Original line number Diff line number Diff line Loading @@ -1801,39 +1801,65 @@ size_t netplus::ssl::sendDataWSA(buffer &data, int flags) { } size_t netplus::ssl::recvDataWSA(buffer &data, int flags) { // 1. Process ciphertext that was placed in _rx_netbuf by the IOCP // 1. Move raw ciphertext from the network buffer (filled by IOCP) to the record processor if (!_rx_netbuf.empty()) { // Append new network data to our decryption record buffer _recv_record.insert(_recv_record.end(), _rx_netbuf.begin(), _rx_netbuf.end()); _rx_netbuf.clear(); // Clear for next WSA request _rx_netbuf.clear(); // Clear to allow the next WSARecv to fill it } // 2. Attempt to decrypt a full TLS record if (_recv_record.size() >= 5) { // Minimum TLS Header size // 2. We need at least 5 bytes to read the TLS Record Header if (_recv_record.size() < 5) { return 0; } // 3. Parse TLS Record Header uint8_t type = _recv_record[0]; uint16_t version = (_recv_record[1] << 8) | _recv_record[2]; uint16_t length = (_recv_record[3] << 8) | _recv_record[4]; if (_recv_record.size() >= (size_t)(5 + length)) { // Validate TLS Major Version (Must be 3 for SSLv3/TLS 1.x) // This specifically prevents the "bad TLS major version" error if ((version >> 8) != 3) { NetException e; e[NetException::Error] << "ssl::recvDataWSA: bad TLS major version: " << (int)(version >> 8); throw e; } // 4. Check if the full encrypted record has arrived if (_recv_record.size() < (size_t)(5 + length)) { return 0; // Wait for more data from the next IOCP completion } // 5. Extract the fragment (ciphertext + MAC + Padding) std::vector<uint8_t> fragment( _recv_record.begin() + 5, _recv_record.begin() + 5 + length ); // 3. Decrypt the fragment using your CBC helper std::vector<uint8_t> plaintext = _decryptRecordCBC(type, version, fragment); // 6. Decryption Logic std::vector<uint8_t> plaintext; if (!_handshakeDone) { // During handshake, data is often unencrypted (ClientHello/ServerHello) // or uses specific handshake encryption states. plaintext = fragment; } else { // Decrypt the application data using the established AES/CBC state plaintext = _decryptRecordCBC(type, version, fragment); } // 4. Copy decrypted plaintext to the user-provided buffer // 7. Copy results to the application buffer size_t toCopy = (std::min)(data.size, plaintext.size()); if (toCopy > 0) { memcpy(data.data.buf, plaintext.data(), toCopy); } // Remove the processed record from the internal buffer // 8. Maintenance: Remove the processed record from the internal stream _recv_record.erase(_recv_record.begin(), _recv_record.begin() + 5 + length); // 9. If there's a protocol-level requirement (like a Handshake Finished), // it should be processed here before returning to the user. return toCopy; } } return 0; // No full record decrypted yet } #endif