#include "tunnel.hpp" #include "log.hpp" #include "stream.hpp" namespace llarp::quic::tunnel { // Takes data from the tcp connection and pushes it down the quic tunnel void on_outgoing_data(uvw::DataEvent& event, uvw::TCPHandle& client) { auto stream = client.data(); assert(stream); std::string_view data{event.data.get(), event.length}; auto peer = client.peer(); llarp::quic::Debug(peer.ip, ":", peer.port, " → lokinet ", llarp::quic::buffer_printer{data}); // Steal the buffer from the DataEvent's unique_ptr: stream->append_buffer(reinterpret_cast(event.data.release()), event.length); if (stream->used() >= PAUSE_SIZE) { llarp::quic::Debug( "quic tunnel is congested (have ", stream->used(), " bytes in flight); pausing local tcp connection reads"); client.stop(); stream->when_available([](llarp::quic::Stream& s) { auto client = s.data(); if (s.used() < PAUSE_SIZE) { llarp::quic::Debug("quic tunnel is no longer congested; resuming tcp connection reading"); client->read(); return true; } return false; }); } else { llarp::quic::Debug("Queued ", event.length, " bytes"); } } // Received data from the quic tunnel and sends it to the TCP connection void on_incoming_data(llarp::quic::Stream& stream, llarp::quic::bstring_view bdata) { auto tcp = stream.data(); assert(tcp); std::string_view data{reinterpret_cast(bdata.data()), bdata.size()}; auto peer = tcp->peer(); llarp::quic::Debug(peer.ip, ":", peer.port, " ← lokinet ", llarp::quic::buffer_printer{data}); if (data.empty()) return; // Try first to write immediately from the existing buffer to avoid needing an // allocation and copy: auto written = tcp->tryWrite(const_cast(data.data()), data.size()); if (written < (int)data.size()) { data.remove_prefix(written); auto wdata = std::make_unique(data.size()); std::copy(data.begin(), data.end(), wdata.get()); tcp->write(std::move(wdata), data.size()); } } void install_stream_forwarding(uvw::TCPHandle& tcp, llarp::quic::Stream& stream) { tcp.data(stream.shared_from_this()); stream.weak_data(tcp.weak_from_this()); tcp.on([](auto&, uvw::TCPHandle& c) { // This fires sometime after we call `close()` to signal that the close is done. llarp::quic::Error( "Connection with ", c.peer().ip, ":", c.peer().port, " closed directly, closing quic stream"); c.data()->close(); }); tcp.on([](auto&, uvw::TCPHandle& c) { // This fires on eof, most likely because the other side of the TCP connection closed it. llarp::quic::Error( "EOF on connection with ", c.peer().ip, ":", c.peer().port, ", closing quic stream"); c.data()->close(); }); tcp.on([](const uvw::ErrorEvent& e, uvw::TCPHandle& tcp) { llarp::quic::Error( "ErrorEvent[", e.name(), ": ", e.what(), "] on connection with ", tcp.peer().ip, ":", tcp.peer().port, ", shutting down quic stream"); // Failed to open connection, so close the quic stream auto stream = tcp.data(); if (stream) stream->close(ERROR_TCP); tcp.close(); }); tcp.on(tunnel::on_outgoing_data); stream.data_callback = on_incoming_data; } } // namespace tunnel