Commit 3359bc13 authored by jan.koester's avatar jan.koester
Browse files

test

parent 6a76e90f
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -329,8 +329,10 @@ namespace netplus {
                // attach accepted socket to con::csock
                if(serverSock->_Type==sockettype::TCP)
                    c->csock = std::make_unique<tcp>(accepted);
                else if(serverSock->_Type==sockettype::SSL)
                     c->csock = std::make_unique<ssl>( static_cast<ssl*>(serverSock)->_cert,accepted);
                else if(serverSock->_Type==sockettype::SSL) {
                    ssl* sslServer = static_cast<ssl*>(serverSock);
                    c->csock = std::make_unique<ssl>(sslServer->getCert(), accepted);
                }
                else
                    continue;

+3 −1
Original line number Diff line number Diff line
@@ -57,7 +57,6 @@
	#include <WinSock2.h>
	#include <ws2tcpip.h>
	#include <mswsock.h>
	typedef unsigned long ULONG_PTR;
#endif

namespace netplus {
@@ -352,6 +351,9 @@ namespace netplus {

		void resetTLS();

		// Public accessor for certificate (used by event loop for SSL accept)
		std::shared_ptr<netplus::x509cert> getCert() const { return _cert; }

	private:
		// --- crypto helpers ---
		std::vector<uint8_t> _sha1_hash(const std::vector<uint8_t>& input);
+51 −125
Original line number Diff line number Diff line
@@ -361,27 +361,24 @@ std::vector<uint8_t> netplus::ssl::readTlsRecordAsync()
    // 1) ensure we have at least TLS record header (5 bytes)
    // ------------------------------------------------------------
    while (_rx_tcp_buf.size() < 5) {
        uint8_t tmp[4096];
        buffer tmpBuf(4096);

        ssize_t r = ::recv(fd(), tmp, sizeof(tmp), 0);
        try {
            size_t r = tcp::recvData(tmpBuf, 0);
            if (r > 0) {
            _rx_tcp_buf.insert(_rx_tcp_buf.end(), tmp, tmp + r);
                _rx_tcp_buf.insert(_rx_tcp_buf.end(), tmpBuf.data.buf, tmpBuf.data.buf + r);
                continue;
            }

        if (r == 0) {
            // peer closed
            // r == 0 means peer closed
            return {};
        }

        if (errno == EAGAIN || errno == EWOULDBLOCK) {
        } catch (NetException& e) {
            if (e.getErrorType() == NetException::Note) {
                NetException n;
                n[NetException::Note] << "ssl: record incomplete (need header)";
                throw n;
            }

        throwSSL(NetException::Error,
                 std::string("recv failed: ") + strerror(errno));
            throw;
        }
    }

    // ------------------------------------------------------------
@@ -394,27 +391,24 @@ std::vector<uint8_t> netplus::ssl::readTlsRecordAsync()
    // 3) read until full record present
    // ------------------------------------------------------------
    while (_rx_tcp_buf.size() < total) {
        uint8_t tmp[4096];
        buffer tmpBuf(4096);

        ssize_t r = ::recv(fd(), tmp, sizeof(tmp), 0);
        try {
            size_t r = tcp::recvData(tmpBuf, 0);
            if (r > 0) {
            _rx_tcp_buf.insert(_rx_tcp_buf.end(), tmp, tmp + r);
                _rx_tcp_buf.insert(_rx_tcp_buf.end(), tmpBuf.data.buf, tmpBuf.data.buf + r);
                continue;
            }

        if (r == 0) {
            // peer closed mid-record
            return {};
        }

        if (errno == EAGAIN || errno == EWOULDBLOCK) {
        } catch (NetException& e) {
            if (e.getErrorType() == NetException::Note) {
                NetException n;
                n[NetException::Note] << "ssl: record incomplete";
            throw n;   // ✅ critical: must be Note!
                throw n;
            }
            throw;
        }

        throwSSL(NetException::Error,
                 std::string("recv failed: ") + strerror(errno));
    }

    // ------------------------------------------------------------
@@ -1679,7 +1673,7 @@ void netplus::ssl::handshake_after_accept(){
                    size_t vend = vp + vlen;
                    while (vp + 1 < vend) {
                        uint16_t v = (uint16_t(ch[vp]) << 8) | ch[vp+1];
                        if (v == 0x0304) offeredTLS13 = true;
                        if (v == 0x0304) return true; // TLS 1.3
                        vp += 2;
                    }
                }
@@ -2506,18 +2500,17 @@ void netplus::ssl::flush_out(){
             _send_record.size() - _send_off
        );

        ssize_t s=0;
        size_t s = 0;

        try {
             s = tcp::sendData(dat, 0);
        } catch (NetException &e) {
            if (e.getErrorType() == NetException::Note) {
                NetException n;
            if(e.getErrorType()==NetException::Error){
                n[NetException::Note] << "ssl::flush_out: would block";
                throw n;
            }
            n[NetException::Error] << "ssl::flush_out send() failed: " << strerror(errno);
            throw n;
            throw;
        }

        if (s == 0) {
@@ -2526,7 +2519,7 @@ void netplus::ssl::flush_out(){
            throw n;
        }

        _send_off += (size_t)s;
        _send_off += s;

        // record fully sent?
        if (_send_off >= _send_record.size()) {
@@ -2554,7 +2547,7 @@ void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& c
    // SSL-Status zurücksetzen
    cssock->_handshakeDone = false;
    cssock->_hs_tx.clear();
    cssock->_rx_netbuf.clear();
    cssock->_rx_tcp_buf.clear();
    cssock->_cert = this->_cert;
    cssock->_rsa = this->_rsa;

@@ -3518,7 +3511,7 @@ size_t netplus::ssl::sendData(buffer& data, int flags) {
        throw n;
    }

    const char* p = data.ptr ? data.data.ptr : data.data.buf;   // <-- VERIFY with your buffer layout
    const char* p = data.data.buf;
    if (!p || data.size == 0) return 0;

    static constexpr size_t TLS_MAX_PLAINTEXT = 16384;
@@ -4150,14 +4143,6 @@ std::vector<uint8_t> netplus::ssl::_rsa_pss_sha256_sign(const std::vector<uint8_

#ifdef Windows

void netplus::ssl::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock, buffer& data){
    // This accept is already implemented above at line ~2545
    // This stub should not be reached - the other implementation handles it
    NetException e;
    e[NetException::Error] << "ssl::accept(LPFN_ACCEPTEX): wrong overload called";
    throw e;
}

void netplus::ssl::sendDataWSA(buffer& data, int flags) {
    // For SSL, we need to encrypt the data before sending
    // The data in buffer is plaintext - encrypt it and queue for sending
@@ -4171,84 +4156,25 @@ void netplus::ssl::sendDataWSA(buffer& data, int flags) {
        std::vector<uint8_t> plain(p, p + data.size);
        
        if (_is_tls13) {
            // TLS 1.3: Use application keys - this queues to _hs_tx
            _tls13_send_record(plain, 0x17, false);  // 0x17 = application_data
            // TLS 1.3: Use application keys
            _tls13_send_record(0x17, plain, false);  // 0x17 = application_data
        } else {
            // TLS 1.2: Use CBC encryption
            std::vector<uint8_t> rec = _buildAppDataRecord(plain);
            queueRaw(rec);
            _sendTLS12Record(0x17, plain);
        }
    }
    
    // Now post the actual WSASend with the encrypted data from _hs_tx
    if (_hs_tx.empty()) return;
    // Now post the actual send using tcp method
    flush_out();
    
    // Copy encrypted data to the buffer's memory for WSASend
    // Resize buffer if needed
    size_t encSize = _hs_tx.size();
    if (encSize > data.size) {
        // Reallocate buffer data
        delete[] data.data.buf;
        data.data.buf = new char[encSize];
        data.size = encSize;
    }
    std::memcpy(data.data.buf, _hs_tx.data(), encSize);
    data.data.len = static_cast<ULONG>(encSize);
    
    // Clear send queue now that we've copied it
    _hs_tx.clear();
    
    DWORD bytesSent = 0;
    DWORD sendFlags = 0;
    
    int result = WSASend(
        _Socket,
        &data.data,  // WSABUF
        1,
        &bytesSent,
        sendFlags,
        &data.overlapped,
        nullptr
    );
    
    if (result == SOCKET_ERROR) {
        int err = WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            NetException e;
            e[NetException::Error] << "ssl::sendDataWSA: WSASend failed: " << err;
            throw e;
        }
    }
    // Use tcp::sendDataWSA for the underlying send
    tcp::sendDataWSA(data, flags);
}

void netplus::ssl::recvDataWSA(buffer& data, int flags) {
    // Post an overlapped receive operation
    // Post an overlapped receive operation using tcp method
    // The received data will be ciphertext that needs decryption
    
    data.wsaBuf.buf = data.data.buf;
    data.wsaBuf.len = static_cast<ULONG>(data.size);
    
    DWORD bytesRecv = 0;
    DWORD recvFlags = 0;
    
    int result = WSARecv(
        _Socket,
        &data.wsaBuf,
        1,
        &bytesRecv,
        &recvFlags,
        &data.overlapped,
        nullptr
    );
    
    if (result == SOCKET_ERROR) {
        int err = WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            NetException e;
            e[NetException::Error] << "ssl::recvDataWSA: WSARecv failed: " << err;
            throw e;
        }
    }
    tcp::recvDataWSA(data, flags);
}

void netplus::ssl::prime_read(buffer& data) {
@@ -4256,8 +4182,8 @@ void netplus::ssl::prime_read(buffer& data) {
    std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED));
    data.operation = OP_READ;
    
    // Post the async receive
    recvDataWSA(data, 0);
    // Post the async receive using tcp method
    tcp::prime_read(data);
}

#endif
+183 −8
Original line number Diff line number Diff line
@@ -146,9 +146,7 @@ void tcp::accept(std::unique_ptr<socket>& csock, bool nonblock) {
}

size_t tcp::sendData(buffer& data, int flags) {
    const char* p = data.ptr ? data.data.ptr : data.data.buf;

    int s = ::send(_Socket, p, (int)data.size, flags);
    int s = ::send(_Socket, data.data.buf, (int)data.size, flags);
    if (s == SOCKET_ERROR) {
        NetException e;
        e[NetException::Error] << "tcp::sendData failed";
@@ -166,15 +164,192 @@ size_t tcp::recvData(buffer& data, int flags) {
    return 0;
}

void tcp::connect(std::unique_ptr<socket>& srvsock) {
    if (::connect(_Socket,
        reinterpret_cast<sockaddr*>(&srvsock->_Addr),
        srvsock->_AddrLen) == SOCKET_ERROR) {
void netplus::tcp::connect(const std::string& addr, int port, bool nonblock)
{
    NetException exception;

    addrinfo hints{};
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    addrinfo* result = nullptr;

    char serv[32];
    std::snprintf(serv, sizeof(serv), "%d", port);

    int gai = ::getaddrinfo(addr.c_str(), serv, &hints, &result);
    if (gai != 0) {
        exception[NetException::Error]
            << "tcp::connect: getaddrinfo failed: " << gai_strerrorA(gai);
        throw exception;
    }

    for (addrinfo* rp = result; rp; rp = rp->ai_next) {

        // -------------------------------------------------------
        // 1) ensure socket exists and matches family
        // -------------------------------------------------------
        if (_Socket != INVALID_SOCKET) {
            // if family mismatch, close and recreate
            sockaddr_storage tmp{};
            int tmpLen = sizeof(tmp);
            if (::getsockname(_Socket, (sockaddr*)&tmp, &tmpLen) == 0) {
                if (tmp.ss_family != rp->ai_family) {
                    ::closesocket(_Socket);
                    _Socket = INVALID_SOCKET;
                }
            }
        }

        if (_Socket == INVALID_SOCKET) {
            _Socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
            if (_Socket == INVALID_SOCKET)
                continue;
        }

        // -------------------------------------------------------
        // 2) set nonblock BEFORE connect() if requested
        // -------------------------------------------------------
        if (nonblock) {
            try {
                setNonBlock();
            } catch (...) {
                ::closesocket(_Socket);
                _Socket = INVALID_SOCKET;
                continue;
            }
        }

        // -------------------------------------------------------
        // 3) connect()
        // -------------------------------------------------------
        if (::connect(_Socket, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
            // connected immediately
            setAddrFromAI(rp);

            ::freeaddrinfo(result);
            return;
        }

        // -------------------------------------------------------
        // 4) nonblocking in progress
        // -------------------------------------------------------
        int err = ::WSAGetLastError();
        if (nonblock && (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS)) {
            setAddrFromAI(rp);

            ::freeaddrinfo(result);

            NetException n;
            n[NetException::Note] << "tcp::connect: WSAEWOULDBLOCK";
            throw n; // caller waits for completion
        }

        // -------------------------------------------------------
        // 5) fatal error -> try next addrinfo
        // -------------------------------------------------------
        ::closesocket(_Socket);
        _Socket = INVALID_SOCKET;
    }

    ::freeaddrinfo(result);

    exception[NetException::Error]
        << "tcp::connect: could not connect to "
        << addr << ":" << port;
    throw exception;
}

void tcp::setBlock() {
    u_long mode = 0;
    if (::ioctlsocket(_Socket, FIONBIO, &mode) == SOCKET_ERROR) {
        NetException e;
        e[NetException::Error] << "tcp::setBlock failed";
        throw e;
    }
}

void tcp::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock, buffer& data) {
    // Create accept socket
    SOCKET acceptSock = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
    if (acceptSock == INVALID_SOCKET) {
        NetException e;
        e[NetException::Error] << "tcp::accept WSASocket failed";
        throw e;
    }

    std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED));
    data.operation = OP_ACCEPT;

    DWORD bytesReceived = 0;
    BOOL result = lpfnAcceptEx(
        _Socket,
        acceptSock,
        data.data.buf,
        0,
        sizeof(SOCKADDR_STORAGE) + 16,
        sizeof(SOCKADDR_STORAGE) + 16,
        &bytesReceived,
        &data.overlapped
    );

    if (!result) {
        int err = ::WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            ::closesocket(acceptSock);
            NetException e;
            e[NetException::Error] << "tcp::accept AcceptEx failed: " << err;
            throw e;
        }
    }

    // Store the accept socket for later retrieval
    *csock = acceptSock;
}

void tcp::sendDataWSA(buffer& data, int flags) {
    DWORD bytesSent = 0;

    std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED));
    data.operation = OP_WRITE;

    int ret = ::WSASend(_Socket, &data.data, 1, &bytesSent, (DWORD)flags,
        &data.overlapped, nullptr);

    if (ret == SOCKET_ERROR) {
        int err = ::WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            NetException e;
            e[NetException::Error] << "tcp::sendDataWSA failed: " << err;
            throw e;
        }
    }
}

void tcp::recvDataWSA(buffer& data, int flags) {
    DWORD bytesRecv = 0;
    DWORD dwFlags = (DWORD)flags;

    std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED));
    data.operation = OP_READ;

    int ret = ::WSARecv(_Socket, &data.data, 1, &bytesRecv, &dwFlags,
        &data.overlapped, nullptr);

    if (ret == SOCKET_ERROR) {
        int err = ::WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            NetException e;
        e[NetException::Error] << "tcp::connect failed";
            e[NetException::Error] << "tcp::recvDataWSA failed: " << err;
            throw e;
        }
    }
}

void tcp::prime_read(buffer& data) {
    recvDataWSA(data, 0);
}

void tcp::getAddress(std::string& addr) {
    char buf[INET6_ADDRSTRLEN]{};
+155 −8
Original line number Diff line number Diff line
@@ -129,10 +129,7 @@ void udp::accept(std::unique_ptr<socket>& csock, bool nonblock) {
}

size_t udp::sendData(buffer& data, int flags) {
    const char* p = data.ptr ? data.data.ptr : data.data.buf;

    int s = ::sendto(_Socket, p, (int)data.size, flags,
        reinterpret_cast<sockaddr*>(&_Addr), _AddrLen);
    int s = ::send(_Socket,data.data.buf,data.data.len, flags);

    if (s == SOCKET_ERROR) {
        NetException e;
@@ -156,10 +153,160 @@ size_t udp::recvData(buffer& data, int flags) {
    return 0;
}

void udp::connect(std::unique_ptr<socket>& srvsock) {
    ::connect(_Socket,
        reinterpret_cast<sockaddr*>(&srvsock->_Addr),
        srvsock->_AddrLen);
void udp::setBlock() {
    u_long mode = 0;
    if (::ioctlsocket(_Socket, FIONBIO, &mode) == SOCKET_ERROR) {
        NetException e;
        e[NetException::Error] << "udp::setBlock failed";
        throw e;
    }
}

void udp::accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock, buffer& data) {
    // UDP does not use AcceptEx - this is a no-op
    (void)lpfnAcceptEx;
    (void)csock;
    (void)data;
}

void udp::sendDataWSA(buffer& data, int flags) {
    DWORD bytesSent = 0;

    std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED));
    data.operation = OP_WRITE;

    int ret = ::WSASend(_Socket, &data.data, 1, &bytesSent, (DWORD)flags,
        &data.overlapped, nullptr);

    if (ret == SOCKET_ERROR) {
        int err = ::WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            NetException e;
            e[NetException::Error] << "udp::sendDataWSA failed: " << err;
            throw e;
        }
    }
}

void udp::recvDataWSA(buffer& data, int flags) {
    DWORD bytesRecv = 0;
    DWORD dwFlags = (DWORD)flags;

    std::memset(&data.overlapped, 0, sizeof(WSAOVERLAPPED));
    data.operation = OP_READ;

    int ret = ::WSARecv(_Socket, &data.data, 1, &bytesRecv, &dwFlags,
        &data.overlapped, nullptr);

    if (ret == SOCKET_ERROR) {
        int err = ::WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            NetException e;
            e[NetException::Error] << "udp::recvDataWSA failed: " << err;
            throw e;
        }
    }
}

void udp::prime_read(buffer& data) {
    recvDataWSA(data, 0);
}

void udp::connect(const std::string& addr, int port, bool nonblock)
{
    NetException exception;

    addrinfo hints{};
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

    addrinfo* result = nullptr;

    char serv[32];
    std::snprintf(serv, sizeof(serv), "%d", port);

    int gai = ::getaddrinfo(addr.c_str(), serv, &hints, &result);
    if (gai != 0) {
        exception[NetException::Error]
            << "udp::connect: getaddrinfo failed: " << gai_strerrorA(gai);
        throw exception;
    }

    for (addrinfo* rp = result; rp; rp = rp->ai_next) {

        // -------------------------------------------------------
        // 1) ensure socket exists and matches family
        // -------------------------------------------------------
        if (_Socket != INVALID_SOCKET) {
            // if family mismatch, close and recreate
            sockaddr_storage tmp{};
            int tmpLen = sizeof(tmp);
            if (::getsockname(_Socket, (sockaddr*)&tmp, &tmpLen) == 0) {
                if (tmp.ss_family != rp->ai_family) {
                    ::closesocket(_Socket);
                    _Socket = INVALID_SOCKET;
                }
            }
        }

        if (_Socket == INVALID_SOCKET) {
            _Socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
            if (_Socket == INVALID_SOCKET)
                continue;
        }

        // -------------------------------------------------------
        // 2) set nonblock BEFORE connect() if requested
        // -------------------------------------------------------
        if (nonblock) {
            try {
                setNonBlock();
            } catch (...) {
                ::closesocket(_Socket);
                _Socket = INVALID_SOCKET;
                continue;
            }
        }

        // -------------------------------------------------------
        // 3) connect()
        // -------------------------------------------------------
        if (::connect(_Socket, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
            // connected immediately
            setAddrFromAI(rp);

            ::freeaddrinfo(result);
            return;
        }

        // -------------------------------------------------------
        // 4) nonblocking in progress
        // -------------------------------------------------------
        int err = ::WSAGetLastError();
        if (nonblock && (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS)) {
            setAddrFromAI(rp);

            ::freeaddrinfo(result);

            NetException n;
            n[NetException::Note] << "udp::connect: WSAEWOULDBLOCK";
            throw n; // caller waits for completion
        }

        // -------------------------------------------------------
        // 5) fatal error -> try next addrinfo
        // -------------------------------------------------------
        ::closesocket(_Socket);
        _Socket = INVALID_SOCKET;
    }

    ::freeaddrinfo(result);

    exception[NetException::Error]
        << "udp::connect: could not connect to "
        << addr << ":" << port;
    throw exception;
}

void udp::getAddress(std::string& addr) {