Loading src/event/iocp.cpp +224 −1646 File changed.Preview size limit exceeded, changes collapsed. Show changes src/socket.h +23 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ #include <array> #include <atomic> #include <string> #include <memory> Loading @@ -48,7 +49,19 @@ typedef unsigned long ULONG_PTR; namespace netplus { enum sockettype {TCP=0,UDP=1,SSL=2}; #ifdef Windows struct AcceptContext { WSAOVERLAPPED ov{}; SOCKET acceptSock = INVALID_SOCKET; // Must persist until AcceptEx completion! // Size recommendation: 2*(sizeof(sockaddr_storage)+16) static constexpr size_t ADDR_BUF_SZ = 2 * (sizeof(SOCKADDR_STORAGE) + 16); alignas(void*) char addrBuf[ADDR_BUF_SZ]{}; }; #endif class buffer { public: buffer(size_t bsize){ Loading Loading @@ -217,11 +230,13 @@ namespace netplus { ssl(const std::string &addr,int port,int maxconnections,int sockopts,const netplus::x509cert &cert); void accept(std::unique_ptr<socket> &csock) override; void handshake_after_accept(); void connect(std::unique_ptr<socket> &csock) override; size_t sendData(buffer &data, int flags = 0) override; size_t recvData(buffer &data, int flags = 0) override; #ifdef Windows void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) override; size_t sendDataWSA(buffer &data, int flags = 0); size_t recvDataWSA(buffer &data, int flags = 0); #endif Loading @@ -230,9 +245,9 @@ namespace netplus { bool hasPendingWrite() const override { return !_send_record.empty(); // your TLS ciphertext buffer } netplus::x509cert _cert; private: // --- Cryptographic Components --- netplus::x509cert _cert; netplus::x509cert _peer_cert; netplus::rsa _rsa; Loading @@ -257,6 +272,9 @@ namespace netplus { std::vector<uint8_t> _recv_record; std::vector<uint8_t> _rx_netbuf; #ifdef Windows std::array<char, 2 * (sizeof(SOCKADDR_STORAGE) + 16)> _acceptBuf{}; #endif size_t _recv_off = 0; std::vector<uint8_t> _handshake_transcript; Loading Loading @@ -315,8 +333,11 @@ namespace netplus { const std::vector<uint8_t>& content ); AcceptContext _acpt; friend class poll; friend class event; friend class EventWorker; friend class EventWorkerArgs; }; }; src/ssl.cpp +100 −46 Original line number Diff line number Diff line Loading @@ -3,7 +3,6 @@ #include <algorithm> #include <iomanip> #include <cstring> #include <memory> #include <vector> #include <random> #include <chrono> Loading Loading @@ -732,18 +731,26 @@ std::vector<uint8_t> netplus::ssl::_calculateHMAC( void netplus::ssl::accept(std::unique_ptr<socket> &csock) { // Legacy synchronous accept + handshake path (used when not using AcceptEx) tcp::accept(csock); if (!csock || csock->fd() == -1) return; if (!csock || csock->fd() == INVALID_SOCKET) return; ssl* cssock = static_cast<ssl*>(csock.get()); // Fresh session state // Copy server credentials/state from listening socket cssock->_cert = this->_cert; cssock->_rsa = this->_rsa; cssock->_send_seq = 0; cssock->_recv_seq = 0; cssock->_handshakeDone = false; cssock->_handshake_transcript.clear(); // Run TLS handshake on the already-accepted socket cssock->handshake_after_accept(); } void netplus::ssl::handshake_after_accept() { // Fresh session state cssock->_send_seq = 0; _recv_seq = 0; _handshakeDone = false; _handshake_transcript.clear(); // ---- BigInt from BE bytes (RSA ciphertext) ------------------------------ auto bigIntFromBytesBE = [&](const uint8_t* bytes, size_t len) -> rsa::bigInt { Loading Loading @@ -787,7 +794,7 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { // ---- Transcript helper: for received handshake records, append rec[5..] -- auto addToTranscript = [&](const std::vector<uint8_t>& rec) { if (rec.size() > 5) { cssock->_handshake_transcript.insert(cssock->_handshake_transcript.end(), _handshake_transcript.insert(_handshake_transcript.end(), rec.begin() + 5, rec.end()); } }; Loading Loading @@ -843,7 +850,7 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { // ---- Handshake ----------------------------------------------------------- try { // 1) Read ClientHello (Handshake record) std::vector<uint8_t> chRec = readTlsRecord(cssock); std::vector<uint8_t> chRec = readTlsRecord(this); if (chRec.size() < 5 + 4 + 2 + 32 || chRec[0] != 0x16 || chRec[5] != 0x01) { throwSSL(netplus::NetException::Error, "Expected ClientHello"); Loading @@ -851,12 +858,12 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { addToTranscript(chRec); // ClientRandom is at: record(5) + hsHdr(4) + version(2) = 11 cssock->_clientRandom.assign(chRec.begin() + 11, chRec.begin() + 43); _clientRandom.assign(chRec.begin() + 11, chRec.begin() + 43); bool secureReneg = clientOfferedSecureReneg(chRec); // 2) Send ServerHello / Certificate / ServerHelloDone _sendServerHello(cssock, secureReneg); _sendServerHello(this, secureReneg); std::vector<uint8_t> certMsg; std::vector<uint8_t> rawCert = _cert.derData; Loading @@ -871,18 +878,18 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { certMsg.push_back(cLen & 0xFF); certMsg.insert(certMsg.end(), rawCert.begin(), rawCert.end()); _sendHandshake(0x0b, certMsg, cssock); // Certificate _sendHandshake(0x0e, {}, cssock); // ServerHelloDone _sendHandshake(0x0b, certMsg, this); // Certificate _sendHandshake(0x0e, {}, this); // ServerHelloDone // 3) Read ClientKeyExchange std::vector<uint8_t> ckeRec = readTlsRecord(cssock); std::vector<uint8_t> ckeRec = readTlsRecord(this); if (ckeRec.size() < 11 || ckeRec[0] != 0x16 || ckeRec[5] != 0x10) { throwSSL(netplus::NetException::Error, "Expected ClientKeyExchange"); } addToTranscript(ckeRec); uint16_t encLen = ((uint16_t)ckeRec[9] << 8) | (uint16_t)ckeRec[10]; const size_t kBytes = (cssock->_rsa.n.bitLength() + 7) / 8; const size_t kBytes = (_rsa.n.bitLength() + 7) / 8; if (encLen != kBytes) { throwSSL(netplus::NetException::Error, "RSA ciphertext length mismatch encLen=" + std::to_string(encLen) + Loading @@ -893,38 +900,38 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { } rsa::bigInt cipher = bigIntFromBytesBE(ckeRec.data() + 11, encLen); rsa::bigInt plainBI = cssock->_rsa.decrypt(cipher); rsa::bigInt plainBI = _rsa.decrypt(cipher); // PKCS#1 block must be exactly kBytes std::vector<uint8_t> pkcs1 = netplus::bigIntToBytesBE(plainBI, kBytes); std::vector<uint8_t> preMasterSecret = extractPreMasterFromPkcs1(pkcs1); // 4) Key derivation (TLS 1.2 PRF uses SHA256; MAC is HMAC-SHA1 for AES128-SHA) std::vector<uint8_t> msSeed = cssock->_clientRandom; msSeed.insert(msSeed.end(), cssock->_serverRandom.begin(), cssock->_serverRandom.end()); std::vector<uint8_t> msSeed = _clientRandom; msSeed.insert(msSeed.end(), _serverRandom.begin(), _serverRandom.end()); std::vector<uint8_t> masterSecret = _prf(preMasterSecret, "master secret", msSeed, 48); std::vector<uint8_t> kbSeed = cssock->_serverRandom; kbSeed.insert(kbSeed.end(), cssock->_clientRandom.begin(), cssock->_clientRandom.end()); 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); size_t off = 0; cssock->_client_mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // client_write_MAC cssock->_mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // server_write_MAC _client_mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // client_write_MAC _mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // server_write_MAC std::vector<uint8_t> clientKey(keyBlock.begin() + off, keyBlock.begin() + off + 16); off += 16; // client_write_key std::vector<uint8_t> serverKey(keyBlock.begin() + off, keyBlock.begin() + off + 16); // server_write_key cssock->_aes_recv = std::make_unique<aes>(clientKey); cssock->_aes = std::make_unique<aes>(serverKey); _aes_recv = std::make_unique<aes>(clientKey); _aes = std::make_unique<aes>(serverKey); // 5) Read Client ChangeCipherSpec (unencrypted) std::vector<uint8_t> ccsRec = readTlsRecord(cssock); std::vector<uint8_t> ccsRec = readTlsRecord(this); if (ccsRec.size() != 6 || ccsRec[0] != 0x14 || ccsRec[5] != 0x01) { throwSSL(netplus::NetException::Error, "Expected ChangeCipherSpec"); } // 6) Read Client Finished (encrypted handshake record) std::vector<uint8_t> finRec = readTlsRecord(cssock); std::vector<uint8_t> finRec = readTlsRecord(this); if (finRec.size() < 5 + 32 || finRec[0] != 0x16) { throwSSL(netplus::NetException::Error, "Expected Finished record"); } Loading @@ -932,7 +939,7 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { std::vector<uint8_t> finFrag(finRec.begin() + 5, finRec.end()); // Decrypt + verify record MAC + increment _recv_seq std::vector<uint8_t> finPT = _decryptRecordCBC(cssock,finRec[0], finVer, finFrag); std::vector<uint8_t> finPT = _decryptRecordCBC(this,finRec[0], finVer, finFrag); // finPT should be the Finished handshake message bytes: // 0x14 00 00 0C + 12 bytes Loading @@ -941,22 +948,22 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { } // Verify client Finished verify_data using transcript hash BEFORE adding Finished std::vector<uint8_t> th = _sha256_hash(cssock->_handshake_transcript); std::vector<uint8_t> th = _sha256_hash(_handshake_transcript); std::vector<uint8_t> expectedClient = _prf(masterSecret, "client finished", th, 12); if (!std::equal(expectedClient.begin(), expectedClient.end(), finPT.begin() + 4)) { throwSSL(netplus::NetException::Error, "client Finished verify_data mismatch"); } // Now add client's Finished handshake message to transcript cssock->_handshake_transcript.insert(cssock->_handshake_transcript.end(), finPT.begin(), finPT.end()); _handshake_transcript.insert(_handshake_transcript.end(), finPT.begin(), finPT.end()); // 7) Send Server ChangeCipherSpec (unencrypted) std::vector<uint8_t> sccs = { 0x14, 0x03, 0x03, 0x00, 0x01, 0x01 }; buffer sccsBuf((const char*)sccs.data(), sccs.size()); cssock->tcp::sendData(sccsBuf); tcp::sendData(sccsBuf); // 8) Send Server Finished (encrypted handshake record) std::vector<uint8_t> th2 = _sha256_hash(cssock->_handshake_transcript); std::vector<uint8_t> th2 = _sha256_hash(_handshake_transcript); std::vector<uint8_t> verifyServer = _prf(masterSecret, "server finished", th2, 12); std::vector<uint8_t> sFin; // handshake message bytes Loading @@ -966,22 +973,76 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { sFin.insert(sFin.end(), verifyServer.begin(), verifyServer.end()); // Add server Finished to transcript (because we send it) cssock->_handshake_transcript.insert(cssock->_handshake_transcript.end(), sFin.begin(), sFin.end()); _handshake_transcript.insert(_handshake_transcript.end(), sFin.begin(), sFin.end()); // Encrypt+MAC as TLS record type Handshake (0x16) _sendEncryptedRecord(cssock,0x16, sFin); _sendEncryptedRecord(this,0x16, sFin); cssock->_handshakeDone = true; _handshakeDone = true; std::cerr << "SSL Handshake Accepted and Secure" << std::endl; } catch (const netplus::NetException& e) { std::cerr << "SSL Accept Error: " << e.what() << std::endl; csock->close(); close(); } catch (const std::exception& e) { throwSSL(netplus::NetException::Error, e.what()); } } #ifdef Windows void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) { NetException exception; // Ensure accepted socket object exists (per-connection ssl instance) if (!csock) { csock = std::make_unique<netplus::ssl>(this->_cert); } ssl* cssock = static_cast<ssl*>(csock.get()); // Create accept socket (overlapped) const addrinfo* ai = reinterpret_cast<const addrinfo*>(this->_SocketInfo); cssock->_Socket = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, nullptr, 0, WSA_FLAG_OVERLAPPED); if (cssock->_Socket == INVALID_SOCKET) { int err = WSAGetLastError(); exception[NetException::Error] << "AcceptEx: WSASocket failed: " << err; throw exception; } // Copy server credentials/state from listener; handshake will be run after AcceptEx completion cssock->_cert = this->_cert; cssock->_rsa = this->_rsa; // Post AcceptEx (addr buffer must live until completion -> stored in cssock->_acceptBuf) DWORD dwbytes = 0; std::memset(&cssock->_Overlapped, 0, sizeof(cssock->_Overlapped)); const BOOL ok = lpfnAcceptEx( (SOCKET)this->_Socket, // listen socket (SOCKET)cssock->_Socket, // accept socket cssock->_acceptBuf.data(), // persistent buffer 0, sizeof(SOCKADDR_STORAGE) + 16, sizeof(SOCKADDR_STORAGE) + 16, &dwbytes, &cssock->_Overlapped ); if (!ok) { int err = WSAGetLastError(); if (err == WSA_IO_PENDING) return; // async path closesocket((SOCKET)cssock->_Socket); cssock->_Socket = INVALID_SOCKET; exception[NetException::Error] << "AcceptEx failed: " << err; throw exception; } // Synchronous completion is possible; completion handler will still run. } #endif void netplus::ssl::connect(std::unique_ptr<socket> &csock) { // 0) Establish underlying TCP connection tcp::connect(csock); Loading Loading @@ -1777,16 +1838,9 @@ bool loadServerPrivateKeyDer(const std::string& keyDerPath); #ifdef Windows size_t netplus::ssl::sendDataWSA(buffer& data, int flags) { // 1) During handshake we may still need to write bytes, but under IOCP we // must NOT call tcp::sendDataWSA here (it would post its own OVERLAPPED). // Instead, queue raw bytes into _send_record and let the IOCP layer WSASend it. // 1) During handshake, we send raw TCP (usually for ServerHello/Certificates) if (!_handshakeDone) { if (!_send_record.empty() && _send_off < _send_record.size()) return 0; if (data.size == 0) return 0; const size_t take = (std::min)((size_t)data.size, (size_t)16384); _send_record.assign((const uint8_t*)data.data.buf, (const uint8_t*)data.data.buf + take); _send_off = 0; return take; return tcp::sendDataWSA(data, flags); } auto throwSSL = [&](int etype, const std::string& msg) -> void { Loading Loading @@ -1862,7 +1916,7 @@ size_t netplus::ssl::sendDataWSA(buffer& data, int flags) { buffer out((const char*)_send_record.data(), _send_record.size()); // This call triggers WSASend with the Overlapped structure. // NOTE: Do not send here. IOCP EventWorker will WSASend _send_record. tcp::sendDataWSA(out, flags); // Return 'take' to inform the application how many plaintext bytes were consumed return take; Loading src/windows/udp.cpp +17 −39 Original line number Diff line number Diff line Loading @@ -85,7 +85,7 @@ netplus::udp::udp(const std::string &addr, int port, int maxconnections, int soc if (_Socket == INVALID_SOCKET) { ::freeaddrinfo(result); exception[NetException::Critical] << "Create Socket " << (int)_Socket << " failed : " << GetLastError(); exception[NetException::Critical] << "Create Socket " << (int)_Socket << " failed : " << WSAGetLastError(); throw exception; } Loading Loading @@ -152,43 +152,21 @@ void netplus::udp::accept(std::unique_ptr<socket> &csock) { (LPINT)&((struct addrinfo*)csock->_SocketInfo)->ai_addrlen, nullptr, 0); if (csock->_Socket == SOCKET_ERROR) { int etype = NetException::Error; if (GetLastError() == WSA_IO_PENDING) if (WSAGetLastError() == WSA_IO_PENDING) etype = NetException::Note; exception[etype] << "Can't accept on Socket: " << GetLastError(); exception[etype] << "Can't accept on Socket: " << WSAGetLastError(); throw exception; } } void netplus::udp::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket> &csock) { (void)lpfnAcceptEx; (void)csock; NetException exception; DWORD dwbytes = 0; CHAR AcceptBuffer[2 * (sizeof(SOCKADDR_IN6) + 16)]; copyAddrInfo(&csock->_SocketInfo, _SocketInfo, _SocketInfoLen); memset(((struct addrinfo*)csock->_SocketInfo)->ai_addr, 0, ((struct addrinfo*)csock->_SocketInfo)->ai_addrlen); ((struct sockaddr_in*)((struct addrinfo*)csock->_SocketInfo)->ai_addr)->sin_port = ((struct sockaddr_in*)((struct addrinfo*)_SocketInfo)->ai_addr)->sin_port; if ((csock->_Socket = WSASocket(((struct addrinfo*)_SocketInfo)->ai_family, ((struct addrinfo*)_SocketInfo)->ai_socktype, 0, nullptr, 0, WSA_FLAG_OVERLAPPED)) < 0) { exception[NetException::Error] << "Accept create Socket faied: failed on " << (int)csock->_Socket << " error code : " << GetLastError(); throw exception; } if (!lpfnAcceptEx(_Socket, csock->_Socket, &AcceptBuffer, 0, sizeof(SOCKADDR_IN6) + 16, sizeof(SOCKADDR_IN6) + 16, &dwbytes, &csock->_Overlapped)) { int etype = NetException::Error; if (GetLastError() == WSA_IO_PENDING) return; exception[etype] << "AcceptEX: failed on " << (int)csock->_Socket << " error code : " << GetLastError(); exception[NetException::Error] << "udp::accept(AcceptEx) not supported (AcceptEx is TCP-only)"; throw exception; } } void netplus::udp::bind() { NetException exception; Loading Loading @@ -227,13 +205,13 @@ size_t netplus::udp::sendData(buffer &data, int flags) { if (_Wait) { // Wait for the overlapped I/O operation to complete. if (WSAWaitForMultipleEvents(1, &_Overlapped.hEvent, FALSE, _Timeout, FALSE) == WSA_WAIT_FAILED) { exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << GetLastError(); exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << WSAGetLastError(); throw exception; } // Get the final result of the overlapped operation. if (!WSAGetOverlappedResult(_Socket, &_Overlapped, &dwBytes, FALSE, (LPDWORD)&flags)) { exception[NetException::Error] << "WSAGetOverlappedResult failed: " << GetLastError(); exception[NetException::Error] << "WSAGetOverlappedResult failed: " << WSAGetLastError(); throw exception; } } Loading Loading @@ -282,13 +260,13 @@ size_t netplus::udp::recvData(buffer &data, int flags) { case WSA_IO_PENDING: if(_Wait){ if (WSAWaitForMultipleEvents(1, &_Overlapped.hEvent, FALSE, _Timeout, FALSE) == WSA_WAIT_FAILED) { exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << GetLastError(); exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << WSAGetLastError(); throw exception; } // Once the wait completes, get the final result. if (!WSAGetOverlappedResult(_Socket, &_Overlapped, &dwBytes, FALSE, (LPDWORD)&flags)) { // An error occurred during the overlapped operation. exception[NetException::Error] << "WSAGetOverlappedResult failed: " << GetLastError(); exception[NetException::Error] << "WSAGetOverlappedResult failed: " << WSAGetLastError(); throw exception; } } Loading @@ -298,10 +276,10 @@ size_t netplus::udp::recvData(buffer &data, int flags) { case WSAECONNRESET: // It's not a connected socket, so these errors are less relevant for UDP. // But we'll keep the logic. exception[NetException::Error] << "Socket recvData failed: " << GetLastError(); exception[NetException::Error] << "Socket recvData failed: " << WSAGetLastError(); throw exception; default: exception[NetException::Error] << "Socket recvData failed some Error on Socket: " << GetLastError(); exception[NetException::Error] << "Socket recvData failed some Error on Socket: " << WSAGetLastError(); throw exception; } } Loading @@ -317,19 +295,19 @@ void netplus::udp::connect(std::unique_ptr<socket> &csock) { if ((_Socket = ::WSASocket(((struct addrinfo*)_SocketInfo)->ai_family, ((struct addrinfo*)_SocketInfo)->ai_socktype, ((struct addrinfo*)_SocketInfo)->ai_protocol, nullptr, 0, WSA_FLAG_OVERLAPPED)) < 0) { NetException exception; exception[NetException::Error] << "Create Socket " << (int)_Socket << " failed : " << GetLastError(); exception[NetException::Error] << "Create Socket " << (int)_Socket << " failed : " << WSAGetLastError(); throw exception; } if (::WSAConnect(_Socket, ((struct addrinfo*)csock->_SocketInfo)->ai_addr, (int)((struct addrinfo*)csock->_SocketInfo)->ai_addrlen, nullptr, nullptr, nullptr, nullptr) < 0) { exception[NetException::Error] << "Socket connect: can't connect to server aborting " << " ErrorMsg:" << GetLastError(); exception[NetException::Error] << "Socket connect: can't connect to server aborting " << " ErrorMsg:" << WSAGetLastError(); throw exception; } if (::getpeername(_Socket, ((struct addrinfo*)_SocketInfo)->ai_addr, (int*)&((struct addrinfo*)_SocketInfo)->ai_addrlen) < 0) { NetException exception; exception[NetException::Error] << "Connect: getpeername failed on " << (int)_Socket << " error code : " << GetLastError(); exception[NetException::Error] << "Connect: getpeername failed on " << (int)_Socket << " error code : " << WSAGetLastError(); throw exception; } Loading Loading
src/event/iocp.cpp +224 −1646 File changed.Preview size limit exceeded, changes collapsed. Show changes
src/socket.h +23 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ #include <array> #include <atomic> #include <string> #include <memory> Loading @@ -48,7 +49,19 @@ typedef unsigned long ULONG_PTR; namespace netplus { enum sockettype {TCP=0,UDP=1,SSL=2}; #ifdef Windows struct AcceptContext { WSAOVERLAPPED ov{}; SOCKET acceptSock = INVALID_SOCKET; // Must persist until AcceptEx completion! // Size recommendation: 2*(sizeof(sockaddr_storage)+16) static constexpr size_t ADDR_BUF_SZ = 2 * (sizeof(SOCKADDR_STORAGE) + 16); alignas(void*) char addrBuf[ADDR_BUF_SZ]{}; }; #endif class buffer { public: buffer(size_t bsize){ Loading Loading @@ -217,11 +230,13 @@ namespace netplus { ssl(const std::string &addr,int port,int maxconnections,int sockopts,const netplus::x509cert &cert); void accept(std::unique_ptr<socket> &csock) override; void handshake_after_accept(); void connect(std::unique_ptr<socket> &csock) override; size_t sendData(buffer &data, int flags = 0) override; size_t recvData(buffer &data, int flags = 0) override; #ifdef Windows void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) override; size_t sendDataWSA(buffer &data, int flags = 0); size_t recvDataWSA(buffer &data, int flags = 0); #endif Loading @@ -230,9 +245,9 @@ namespace netplus { bool hasPendingWrite() const override { return !_send_record.empty(); // your TLS ciphertext buffer } netplus::x509cert _cert; private: // --- Cryptographic Components --- netplus::x509cert _cert; netplus::x509cert _peer_cert; netplus::rsa _rsa; Loading @@ -257,6 +272,9 @@ namespace netplus { std::vector<uint8_t> _recv_record; std::vector<uint8_t> _rx_netbuf; #ifdef Windows std::array<char, 2 * (sizeof(SOCKADDR_STORAGE) + 16)> _acceptBuf{}; #endif size_t _recv_off = 0; std::vector<uint8_t> _handshake_transcript; Loading Loading @@ -315,8 +333,11 @@ namespace netplus { const std::vector<uint8_t>& content ); AcceptContext _acpt; friend class poll; friend class event; friend class EventWorker; friend class EventWorkerArgs; }; };
src/ssl.cpp +100 −46 Original line number Diff line number Diff line Loading @@ -3,7 +3,6 @@ #include <algorithm> #include <iomanip> #include <cstring> #include <memory> #include <vector> #include <random> #include <chrono> Loading Loading @@ -732,18 +731,26 @@ std::vector<uint8_t> netplus::ssl::_calculateHMAC( void netplus::ssl::accept(std::unique_ptr<socket> &csock) { // Legacy synchronous accept + handshake path (used when not using AcceptEx) tcp::accept(csock); if (!csock || csock->fd() == -1) return; if (!csock || csock->fd() == INVALID_SOCKET) return; ssl* cssock = static_cast<ssl*>(csock.get()); // Fresh session state // Copy server credentials/state from listening socket cssock->_cert = this->_cert; cssock->_rsa = this->_rsa; cssock->_send_seq = 0; cssock->_recv_seq = 0; cssock->_handshakeDone = false; cssock->_handshake_transcript.clear(); // Run TLS handshake on the already-accepted socket cssock->handshake_after_accept(); } void netplus::ssl::handshake_after_accept() { // Fresh session state cssock->_send_seq = 0; _recv_seq = 0; _handshakeDone = false; _handshake_transcript.clear(); // ---- BigInt from BE bytes (RSA ciphertext) ------------------------------ auto bigIntFromBytesBE = [&](const uint8_t* bytes, size_t len) -> rsa::bigInt { Loading Loading @@ -787,7 +794,7 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { // ---- Transcript helper: for received handshake records, append rec[5..] -- auto addToTranscript = [&](const std::vector<uint8_t>& rec) { if (rec.size() > 5) { cssock->_handshake_transcript.insert(cssock->_handshake_transcript.end(), _handshake_transcript.insert(_handshake_transcript.end(), rec.begin() + 5, rec.end()); } }; Loading Loading @@ -843,7 +850,7 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { // ---- Handshake ----------------------------------------------------------- try { // 1) Read ClientHello (Handshake record) std::vector<uint8_t> chRec = readTlsRecord(cssock); std::vector<uint8_t> chRec = readTlsRecord(this); if (chRec.size() < 5 + 4 + 2 + 32 || chRec[0] != 0x16 || chRec[5] != 0x01) { throwSSL(netplus::NetException::Error, "Expected ClientHello"); Loading @@ -851,12 +858,12 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { addToTranscript(chRec); // ClientRandom is at: record(5) + hsHdr(4) + version(2) = 11 cssock->_clientRandom.assign(chRec.begin() + 11, chRec.begin() + 43); _clientRandom.assign(chRec.begin() + 11, chRec.begin() + 43); bool secureReneg = clientOfferedSecureReneg(chRec); // 2) Send ServerHello / Certificate / ServerHelloDone _sendServerHello(cssock, secureReneg); _sendServerHello(this, secureReneg); std::vector<uint8_t> certMsg; std::vector<uint8_t> rawCert = _cert.derData; Loading @@ -871,18 +878,18 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { certMsg.push_back(cLen & 0xFF); certMsg.insert(certMsg.end(), rawCert.begin(), rawCert.end()); _sendHandshake(0x0b, certMsg, cssock); // Certificate _sendHandshake(0x0e, {}, cssock); // ServerHelloDone _sendHandshake(0x0b, certMsg, this); // Certificate _sendHandshake(0x0e, {}, this); // ServerHelloDone // 3) Read ClientKeyExchange std::vector<uint8_t> ckeRec = readTlsRecord(cssock); std::vector<uint8_t> ckeRec = readTlsRecord(this); if (ckeRec.size() < 11 || ckeRec[0] != 0x16 || ckeRec[5] != 0x10) { throwSSL(netplus::NetException::Error, "Expected ClientKeyExchange"); } addToTranscript(ckeRec); uint16_t encLen = ((uint16_t)ckeRec[9] << 8) | (uint16_t)ckeRec[10]; const size_t kBytes = (cssock->_rsa.n.bitLength() + 7) / 8; const size_t kBytes = (_rsa.n.bitLength() + 7) / 8; if (encLen != kBytes) { throwSSL(netplus::NetException::Error, "RSA ciphertext length mismatch encLen=" + std::to_string(encLen) + Loading @@ -893,38 +900,38 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { } rsa::bigInt cipher = bigIntFromBytesBE(ckeRec.data() + 11, encLen); rsa::bigInt plainBI = cssock->_rsa.decrypt(cipher); rsa::bigInt plainBI = _rsa.decrypt(cipher); // PKCS#1 block must be exactly kBytes std::vector<uint8_t> pkcs1 = netplus::bigIntToBytesBE(plainBI, kBytes); std::vector<uint8_t> preMasterSecret = extractPreMasterFromPkcs1(pkcs1); // 4) Key derivation (TLS 1.2 PRF uses SHA256; MAC is HMAC-SHA1 for AES128-SHA) std::vector<uint8_t> msSeed = cssock->_clientRandom; msSeed.insert(msSeed.end(), cssock->_serverRandom.begin(), cssock->_serverRandom.end()); std::vector<uint8_t> msSeed = _clientRandom; msSeed.insert(msSeed.end(), _serverRandom.begin(), _serverRandom.end()); std::vector<uint8_t> masterSecret = _prf(preMasterSecret, "master secret", msSeed, 48); std::vector<uint8_t> kbSeed = cssock->_serverRandom; kbSeed.insert(kbSeed.end(), cssock->_clientRandom.begin(), cssock->_clientRandom.end()); 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); size_t off = 0; cssock->_client_mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // client_write_MAC cssock->_mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // server_write_MAC _client_mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // client_write_MAC _mac_key.assign(keyBlock.begin() + off, keyBlock.begin() + off + 20); off += 20; // server_write_MAC std::vector<uint8_t> clientKey(keyBlock.begin() + off, keyBlock.begin() + off + 16); off += 16; // client_write_key std::vector<uint8_t> serverKey(keyBlock.begin() + off, keyBlock.begin() + off + 16); // server_write_key cssock->_aes_recv = std::make_unique<aes>(clientKey); cssock->_aes = std::make_unique<aes>(serverKey); _aes_recv = std::make_unique<aes>(clientKey); _aes = std::make_unique<aes>(serverKey); // 5) Read Client ChangeCipherSpec (unencrypted) std::vector<uint8_t> ccsRec = readTlsRecord(cssock); std::vector<uint8_t> ccsRec = readTlsRecord(this); if (ccsRec.size() != 6 || ccsRec[0] != 0x14 || ccsRec[5] != 0x01) { throwSSL(netplus::NetException::Error, "Expected ChangeCipherSpec"); } // 6) Read Client Finished (encrypted handshake record) std::vector<uint8_t> finRec = readTlsRecord(cssock); std::vector<uint8_t> finRec = readTlsRecord(this); if (finRec.size() < 5 + 32 || finRec[0] != 0x16) { throwSSL(netplus::NetException::Error, "Expected Finished record"); } Loading @@ -932,7 +939,7 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { std::vector<uint8_t> finFrag(finRec.begin() + 5, finRec.end()); // Decrypt + verify record MAC + increment _recv_seq std::vector<uint8_t> finPT = _decryptRecordCBC(cssock,finRec[0], finVer, finFrag); std::vector<uint8_t> finPT = _decryptRecordCBC(this,finRec[0], finVer, finFrag); // finPT should be the Finished handshake message bytes: // 0x14 00 00 0C + 12 bytes Loading @@ -941,22 +948,22 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { } // Verify client Finished verify_data using transcript hash BEFORE adding Finished std::vector<uint8_t> th = _sha256_hash(cssock->_handshake_transcript); std::vector<uint8_t> th = _sha256_hash(_handshake_transcript); std::vector<uint8_t> expectedClient = _prf(masterSecret, "client finished", th, 12); if (!std::equal(expectedClient.begin(), expectedClient.end(), finPT.begin() + 4)) { throwSSL(netplus::NetException::Error, "client Finished verify_data mismatch"); } // Now add client's Finished handshake message to transcript cssock->_handshake_transcript.insert(cssock->_handshake_transcript.end(), finPT.begin(), finPT.end()); _handshake_transcript.insert(_handshake_transcript.end(), finPT.begin(), finPT.end()); // 7) Send Server ChangeCipherSpec (unencrypted) std::vector<uint8_t> sccs = { 0x14, 0x03, 0x03, 0x00, 0x01, 0x01 }; buffer sccsBuf((const char*)sccs.data(), sccs.size()); cssock->tcp::sendData(sccsBuf); tcp::sendData(sccsBuf); // 8) Send Server Finished (encrypted handshake record) std::vector<uint8_t> th2 = _sha256_hash(cssock->_handshake_transcript); std::vector<uint8_t> th2 = _sha256_hash(_handshake_transcript); std::vector<uint8_t> verifyServer = _prf(masterSecret, "server finished", th2, 12); std::vector<uint8_t> sFin; // handshake message bytes Loading @@ -966,22 +973,76 @@ void netplus::ssl::accept(std::unique_ptr<socket> &csock) { sFin.insert(sFin.end(), verifyServer.begin(), verifyServer.end()); // Add server Finished to transcript (because we send it) cssock->_handshake_transcript.insert(cssock->_handshake_transcript.end(), sFin.begin(), sFin.end()); _handshake_transcript.insert(_handshake_transcript.end(), sFin.begin(), sFin.end()); // Encrypt+MAC as TLS record type Handshake (0x16) _sendEncryptedRecord(cssock,0x16, sFin); _sendEncryptedRecord(this,0x16, sFin); cssock->_handshakeDone = true; _handshakeDone = true; std::cerr << "SSL Handshake Accepted and Secure" << std::endl; } catch (const netplus::NetException& e) { std::cerr << "SSL Accept Error: " << e.what() << std::endl; csock->close(); close(); } catch (const std::exception& e) { throwSSL(netplus::NetException::Error, e.what()); } } #ifdef Windows void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) { NetException exception; // Ensure accepted socket object exists (per-connection ssl instance) if (!csock) { csock = std::make_unique<netplus::ssl>(this->_cert); } ssl* cssock = static_cast<ssl*>(csock.get()); // Create accept socket (overlapped) const addrinfo* ai = reinterpret_cast<const addrinfo*>(this->_SocketInfo); cssock->_Socket = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, nullptr, 0, WSA_FLAG_OVERLAPPED); if (cssock->_Socket == INVALID_SOCKET) { int err = WSAGetLastError(); exception[NetException::Error] << "AcceptEx: WSASocket failed: " << err; throw exception; } // Copy server credentials/state from listener; handshake will be run after AcceptEx completion cssock->_cert = this->_cert; cssock->_rsa = this->_rsa; // Post AcceptEx (addr buffer must live until completion -> stored in cssock->_acceptBuf) DWORD dwbytes = 0; std::memset(&cssock->_Overlapped, 0, sizeof(cssock->_Overlapped)); const BOOL ok = lpfnAcceptEx( (SOCKET)this->_Socket, // listen socket (SOCKET)cssock->_Socket, // accept socket cssock->_acceptBuf.data(), // persistent buffer 0, sizeof(SOCKADDR_STORAGE) + 16, sizeof(SOCKADDR_STORAGE) + 16, &dwbytes, &cssock->_Overlapped ); if (!ok) { int err = WSAGetLastError(); if (err == WSA_IO_PENDING) return; // async path closesocket((SOCKET)cssock->_Socket); cssock->_Socket = INVALID_SOCKET; exception[NetException::Error] << "AcceptEx failed: " << err; throw exception; } // Synchronous completion is possible; completion handler will still run. } #endif void netplus::ssl::connect(std::unique_ptr<socket> &csock) { // 0) Establish underlying TCP connection tcp::connect(csock); Loading Loading @@ -1777,16 +1838,9 @@ bool loadServerPrivateKeyDer(const std::string& keyDerPath); #ifdef Windows size_t netplus::ssl::sendDataWSA(buffer& data, int flags) { // 1) During handshake we may still need to write bytes, but under IOCP we // must NOT call tcp::sendDataWSA here (it would post its own OVERLAPPED). // Instead, queue raw bytes into _send_record and let the IOCP layer WSASend it. // 1) During handshake, we send raw TCP (usually for ServerHello/Certificates) if (!_handshakeDone) { if (!_send_record.empty() && _send_off < _send_record.size()) return 0; if (data.size == 0) return 0; const size_t take = (std::min)((size_t)data.size, (size_t)16384); _send_record.assign((const uint8_t*)data.data.buf, (const uint8_t*)data.data.buf + take); _send_off = 0; return take; return tcp::sendDataWSA(data, flags); } auto throwSSL = [&](int etype, const std::string& msg) -> void { Loading Loading @@ -1862,7 +1916,7 @@ size_t netplus::ssl::sendDataWSA(buffer& data, int flags) { buffer out((const char*)_send_record.data(), _send_record.size()); // This call triggers WSASend with the Overlapped structure. // NOTE: Do not send here. IOCP EventWorker will WSASend _send_record. tcp::sendDataWSA(out, flags); // Return 'take' to inform the application how many plaintext bytes were consumed return take; Loading
src/windows/udp.cpp +17 −39 Original line number Diff line number Diff line Loading @@ -85,7 +85,7 @@ netplus::udp::udp(const std::string &addr, int port, int maxconnections, int soc if (_Socket == INVALID_SOCKET) { ::freeaddrinfo(result); exception[NetException::Critical] << "Create Socket " << (int)_Socket << " failed : " << GetLastError(); exception[NetException::Critical] << "Create Socket " << (int)_Socket << " failed : " << WSAGetLastError(); throw exception; } Loading Loading @@ -152,43 +152,21 @@ void netplus::udp::accept(std::unique_ptr<socket> &csock) { (LPINT)&((struct addrinfo*)csock->_SocketInfo)->ai_addrlen, nullptr, 0); if (csock->_Socket == SOCKET_ERROR) { int etype = NetException::Error; if (GetLastError() == WSA_IO_PENDING) if (WSAGetLastError() == WSA_IO_PENDING) etype = NetException::Note; exception[etype] << "Can't accept on Socket: " << GetLastError(); exception[etype] << "Can't accept on Socket: " << WSAGetLastError(); throw exception; } } void netplus::udp::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket> &csock) { (void)lpfnAcceptEx; (void)csock; NetException exception; DWORD dwbytes = 0; CHAR AcceptBuffer[2 * (sizeof(SOCKADDR_IN6) + 16)]; copyAddrInfo(&csock->_SocketInfo, _SocketInfo, _SocketInfoLen); memset(((struct addrinfo*)csock->_SocketInfo)->ai_addr, 0, ((struct addrinfo*)csock->_SocketInfo)->ai_addrlen); ((struct sockaddr_in*)((struct addrinfo*)csock->_SocketInfo)->ai_addr)->sin_port = ((struct sockaddr_in*)((struct addrinfo*)_SocketInfo)->ai_addr)->sin_port; if ((csock->_Socket = WSASocket(((struct addrinfo*)_SocketInfo)->ai_family, ((struct addrinfo*)_SocketInfo)->ai_socktype, 0, nullptr, 0, WSA_FLAG_OVERLAPPED)) < 0) { exception[NetException::Error] << "Accept create Socket faied: failed on " << (int)csock->_Socket << " error code : " << GetLastError(); throw exception; } if (!lpfnAcceptEx(_Socket, csock->_Socket, &AcceptBuffer, 0, sizeof(SOCKADDR_IN6) + 16, sizeof(SOCKADDR_IN6) + 16, &dwbytes, &csock->_Overlapped)) { int etype = NetException::Error; if (GetLastError() == WSA_IO_PENDING) return; exception[etype] << "AcceptEX: failed on " << (int)csock->_Socket << " error code : " << GetLastError(); exception[NetException::Error] << "udp::accept(AcceptEx) not supported (AcceptEx is TCP-only)"; throw exception; } } void netplus::udp::bind() { NetException exception; Loading Loading @@ -227,13 +205,13 @@ size_t netplus::udp::sendData(buffer &data, int flags) { if (_Wait) { // Wait for the overlapped I/O operation to complete. if (WSAWaitForMultipleEvents(1, &_Overlapped.hEvent, FALSE, _Timeout, FALSE) == WSA_WAIT_FAILED) { exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << GetLastError(); exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << WSAGetLastError(); throw exception; } // Get the final result of the overlapped operation. if (!WSAGetOverlappedResult(_Socket, &_Overlapped, &dwBytes, FALSE, (LPDWORD)&flags)) { exception[NetException::Error] << "WSAGetOverlappedResult failed: " << GetLastError(); exception[NetException::Error] << "WSAGetOverlappedResult failed: " << WSAGetLastError(); throw exception; } } Loading Loading @@ -282,13 +260,13 @@ size_t netplus::udp::recvData(buffer &data, int flags) { case WSA_IO_PENDING: if(_Wait){ if (WSAWaitForMultipleEvents(1, &_Overlapped.hEvent, FALSE, _Timeout, FALSE) == WSA_WAIT_FAILED) { exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << GetLastError(); exception[NetException::Error] << "WSAWaitForMultipleEvents failed: " << WSAGetLastError(); throw exception; } // Once the wait completes, get the final result. if (!WSAGetOverlappedResult(_Socket, &_Overlapped, &dwBytes, FALSE, (LPDWORD)&flags)) { // An error occurred during the overlapped operation. exception[NetException::Error] << "WSAGetOverlappedResult failed: " << GetLastError(); exception[NetException::Error] << "WSAGetOverlappedResult failed: " << WSAGetLastError(); throw exception; } } Loading @@ -298,10 +276,10 @@ size_t netplus::udp::recvData(buffer &data, int flags) { case WSAECONNRESET: // It's not a connected socket, so these errors are less relevant for UDP. // But we'll keep the logic. exception[NetException::Error] << "Socket recvData failed: " << GetLastError(); exception[NetException::Error] << "Socket recvData failed: " << WSAGetLastError(); throw exception; default: exception[NetException::Error] << "Socket recvData failed some Error on Socket: " << GetLastError(); exception[NetException::Error] << "Socket recvData failed some Error on Socket: " << WSAGetLastError(); throw exception; } } Loading @@ -317,19 +295,19 @@ void netplus::udp::connect(std::unique_ptr<socket> &csock) { if ((_Socket = ::WSASocket(((struct addrinfo*)_SocketInfo)->ai_family, ((struct addrinfo*)_SocketInfo)->ai_socktype, ((struct addrinfo*)_SocketInfo)->ai_protocol, nullptr, 0, WSA_FLAG_OVERLAPPED)) < 0) { NetException exception; exception[NetException::Error] << "Create Socket " << (int)_Socket << " failed : " << GetLastError(); exception[NetException::Error] << "Create Socket " << (int)_Socket << " failed : " << WSAGetLastError(); throw exception; } if (::WSAConnect(_Socket, ((struct addrinfo*)csock->_SocketInfo)->ai_addr, (int)((struct addrinfo*)csock->_SocketInfo)->ai_addrlen, nullptr, nullptr, nullptr, nullptr) < 0) { exception[NetException::Error] << "Socket connect: can't connect to server aborting " << " ErrorMsg:" << GetLastError(); exception[NetException::Error] << "Socket connect: can't connect to server aborting " << " ErrorMsg:" << WSAGetLastError(); throw exception; } if (::getpeername(_Socket, ((struct addrinfo*)_SocketInfo)->ai_addr, (int*)&((struct addrinfo*)_SocketInfo)->ai_addrlen) < 0) { NetException exception; exception[NetException::Error] << "Connect: getpeername failed on " << (int)_Socket << " error code : " << GetLastError(); exception[NetException::Error] << "Connect: getpeername failed on " << (int)_Socket << " error code : " << WSAGetLastError(); throw exception; } Loading