Commit 24eced9a authored by jan.koester's avatar jan.koester
Browse files

bugfixes

parent 29a3b46b
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -37,6 +37,28 @@ namespace netplus {
    }

    // Helper function to extract SNI from ClientHello
    // ALPN (RFC 7301) extension carrying exactly one protocol -- what a server answers with.
    static void appendAlpnExtension(std::vector<uint8_t>& exts, const std::string& proto) {
        if (proto.empty() || proto.size() > 255) return;
        const uint16_t list_len = uint16_t(1 + proto.size());
        const uint16_t ext_len = uint16_t(2 + list_len);
        exts.push_back(0x00); exts.push_back(0x10);
        exts.push_back(uint8_t(ext_len >> 8)); exts.push_back(uint8_t(ext_len & 0xFF));
        exts.push_back(uint8_t(list_len >> 8)); exts.push_back(uint8_t(list_len & 0xFF));
        exts.push_back(uint8_t(proto.size()));
        exts.insert(exts.end(), proto.begin(), proto.end());
    }

    // The protocol a server selected, from its ALPN extension data (list_len(2) + one
    // len-prefixed name). Empty if malformed.
    static std::string parseAlpnSelection(const uint8_t* ep, size_t el) {
        if (el < 3) return {};
        size_t list_len = (size_t(ep[0]) << 8) | ep[1];
        size_t name_len = ep[2];
        if (list_len + 2 != el || name_len == 0 || 3 + name_len > el) return {};
        return std::string(reinterpret_cast<const char*>(ep + 3), name_len);
    }

    static bool extractSNIFromClientHello(const std::vector<uint8_t>& ch, std::string& hostname) {
        // ClientHello format: type(1) + length(3) + version(2) + random(32) + session_id_len(1) + ...
        if (ch.size() < 43) return false;
@@ -770,6 +792,7 @@ namespace netplus {
                                exts.push_back(0x00); exts.push_back(0x17);
                                exts.push_back(0x00); exts.push_back(0x00);
                            }
                            appendAlpnExtension(exts, selected_alpn);
                            body.push_back((exts.size() >> 8) & 0xFF);
                            body.push_back((exts.size() >> 0) & 0xFF);
                            body.insert(body.end(), exts.begin(), exts.end());
@@ -1706,6 +1729,11 @@ namespace netplus {

                        const uint8_t* ep = p + eoff;

                        // ALPN: a TLS 1.2 server answers here (TLS 1.3: EncryptedExtensions)
                        if (et == 0x0010) {
                            selected_alpn = parseAlpnSelection(ep, el);
                        }

                        // supported_versions (0x002b): for ServerHello it's 2 bytes selected version
                        if (et == 0x002b && el == 2) {
                            uint16_t sel = (uint16_t(ep[0]) << 8) | ep[1];
@@ -1898,6 +1926,21 @@ namespace netplus {
                        throw e;
                    }

                    // ALPN: TLS 1.3 servers answer in EncryptedExtensions (type(1)+len(3)+
                    // extensions_length(2)+extensions).
                    if (msg.size() >= 6) {
                        size_t eoff = 6;
                        size_t eend = std::min(msg.size(), 6 + ((size_t(msg[4]) << 8) | msg[5]));
                        while (eoff + 4 <= eend) {
                            uint16_t et = (uint16_t(msg[eoff]) << 8) | msg[eoff+1];
                            uint16_t el = (uint16_t(msg[eoff+2]) << 8) | msg[eoff+3];
                            eoff += 4;
                            if (eoff + el > eend) break;
                            if (et == 0x0010) selected_alpn = parseAlpnSelection(msg.data() + eoff, el);
                            eoff += el;
                        }
                    }

                    // Add to transcript
                    handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end());

@@ -4315,6 +4358,10 @@ namespace netplus {
            exts.push_back(0x00); exts.push_back(0x00);
        }

        // ALPN (RFC 7301): TLS 1.2 answers in ServerHello -- it used to be negotiated
        // server-side but never sent back, so the client couldn't know the result.
        appendAlpnExtension(exts, selected_alpn);

        // Write extensions length (always present in TLS1.2 ServerHello!)
        body.push_back((exts.size() >> 8) & 0xFF);
        body.push_back((exts.size() >> 0) & 0xFF);
@@ -4941,6 +4988,19 @@ namespace netplus {
        exts.push_back(0x00); exts.push_back(0x17);
        exts.push_back(0x00); exts.push_back(0x00);

        // ALPN (RFC 7301). client_alpn_protocols is the wire-format protocol_name_list body
        // (length-prefixed names, e.g. "\x02h2\x08http/1.1"). It used to be set by callers but
        // never sent, so every server fell back to its default (HTTP/1.1).
        if (!client_alpn_protocols.empty() && client_alpn_protocols.size() <= 0xFFFD) {
            const uint16_t list_len = uint16_t(client_alpn_protocols.size());
            const uint16_t ext_len = uint16_t(2 + list_len);
            exts.push_back(0x00); exts.push_back(0x10);
            exts.push_back(uint8_t(ext_len >> 8)); exts.push_back(uint8_t(ext_len & 0xFF));
            exts.push_back(uint8_t(list_len >> 8)); exts.push_back(uint8_t(list_len & 0xFF));
            exts.insert(exts.end(), client_alpn_protocols.begin(), client_alpn_protocols.end());
        }
        selected_alpn.clear();

        // Mark that we offered TLS 1.3 (only when not in TLS 1.2-only mode)
        client_offered_tls13 = !client_tls12_only;

+25 −0
Original line number Diff line number Diff line
@@ -4945,6 +4945,31 @@ void quic::flushFlowControl() {
    flushBatch();
}

size_t quic::openStreamCount(bool bidirectionalOnly) const {
    std::lock_guard<std::recursive_mutex> lock(quic_mtx());
    if (!bidirectionalOnly) return _streams.size();
    size_t n = 0;
    for (const auto &[id, st] : _streams)
        if ((id & 0x2) == 0) ++n; // RFC 9000 §2.1: bit 1 clear = bidirectional
    return n;
}

size_t quic::childOpenStreamCount(bool bidirectionalOnly) const {
    // Pin first, count after: each child's own lock is taken outside _registry_mutex, and a
    // child is registered under two CIDs -- count it once.
    std::vector<std::shared_ptr<quic>> pins;
    {
        std::shared_lock<netplus::WriterPreferringSharedMutex> lk(_registry_mutex);
        for (const auto &[cid, child] : _child_connections) {
            if (child && std::find(pins.begin(), pins.end(), child) == pins.end())
                pins.push_back(child);
        }
    }
    size_t total = 0;
    for (const auto &c : pins) total += c->openStreamCount(bidirectionalOnly);
    return total;
}

bool quic::isStreamSendStopped(uint64_t stream_id) const {
    std::lock_guard<std::recursive_mutex> lock(quic_mtx());
    auto it = _streams.find(stream_id);
+9 −0
Original line number Diff line number Diff line
@@ -892,6 +892,15 @@ namespace netplus {
		// stays readable until closeStream().
		bool isStreamSendStopped(uint64_t stream_id) const;

		// Diagnostics: streams this connection still tracks, i.e. not yet retired (a stream
		// retires once both directions finished and the application called closeStream()
		// or resetStream()). A count that only grows means streams are leaking.
		// bidirectionalOnly: skip unidirectional streams (e.g. HTTP/3's control and QPACK
		// streams, which live as long as the connection by design).
		size_t openStreamCount(bool bidirectionalOnly = false) const;
		// Listener: openStreamCount() summed over its accepted connections.
		size_t childOpenStreamCount(bool bidirectionalOnly = false) const;

        virtual std::string selectStreamProtocol(const std::string &proto) { return ""; }
		
		// Abort a stream abnormally (RFC 9000 §19.4 RESET_STREAM). Use this