---
title: llarp/quic/tunnel.hpp

---

# llarp/quic/tunnel.hpp



## Namespaces

| Name           |
| -------------- |
| **[llarp](Namespaces/namespacellarp.md)** <br>[crypto.hpp] |
| **[llarp::quic](Namespaces/namespacellarp_1_1quic.md)**  |
| **[llarp::quic::tunnel](Namespaces/namespacellarp_1_1quic_1_1tunnel.md)**  |

## Classes

|                | Name           |
| -------------- | -------------- |
| class | **[llarp::quic::TunnelManager](Classes/classllarp_1_1quic_1_1TunnelManager.md)** <br>Manager class for incoming and outgoing QUIC tunnels.  |




## Source code

```cpp
#pragma once

#include <llarp/endpoint_base.hpp>
#include "stream.hpp"
#include "address.hpp"
#include "client.hpp"
#include "server.hpp"

#include <charconv>
#include <cstdint>
#include <string>
#include <string_view>

#include <uvw/tcp.h>

namespace llarp::quic
{
  namespace tunnel
  {
    // The server sends back a 0x00 to signal that the remote TCP connection was established and
    // that it is now accepting stream data; the client is not allowed to send any other data down
    // the stream until this comes back (any data sent down the stream before then is discarded.)
    inline constexpr std::byte CONNECT_INIT{0x00};
    // QUIC application error codes we sent on failures:
    // Failure to establish an initial connection:
    inline constexpr uint64_t ERROR_CONNECT{0x5471907};
    // Error if we receive something other than CONNECT_INIT as the initial stream data from the
    // server
    inline constexpr uint64_t ERROR_BAD_INIT{0x5471908};
    // Close error code sent if we get an error on the TCP socket (other than an initial connect
    // failure)
    inline constexpr uint64_t ERROR_TCP{0x5471909};

    // We pause reading from the local TCP socket if we have more than this amount of outstanding
    // unacked data in the quic tunnel, then resume once it drops below this.
    inline constexpr size_t PAUSE_SIZE = 64 * 1024;
  }  // namespace tunnel

  class TunnelManager
  {
   public:
    using ListenHandler = std::function<std::optional<SockAddr>(
        std::string_view lokinet_addr,  // The remote's full lokinet address
        uint16_t port                   // The requested port the tunnel wants to reach
        )>;

    // Timeout for the next `open()`.  Note that when `open()` is given a ONS name to resolve this
    // includes the resolution time.
    std::chrono::milliseconds open_timeout = 4s;

    TunnelManager(EndpointBase& endpoint);

    int
    listen(ListenHandler handler);

    int
    listen(SockAddr port);

    void
    forget(int id);

    using OpenCallback = std::function<void(bool success)>;

    std::pair<SockAddr, uint16_t>
    open(
        std::string_view remote_addr,
        uint16_t port,
        OpenCallback on_open = {},
        SockAddr bind_addr = {127, 0, 0, 1});

    void
    close(int id);

    void
    receive_packet(const service::ConvoTag& tag, const llarp_buffer_t& buf);

    inline bool
    hasListeners() const
    {
      return not incoming_handlers_.empty();
    }

   private:
    EndpointBase& service_endpoint_;

    struct ClientTunnel
    {
      // quic endpoint
      std::unique_ptr<Client> client;
      // Callback to invoke on quic connection established (true argument) or failed (false arg)
      OpenCallback open_cb;
      // TCP listening socket
      std::shared_ptr<uvw::TCPHandle> tcp;
      // Accepted TCP connections
      std::unordered_set<std::shared_ptr<uvw::TCPHandle>> conns;
      // Queue of incoming connections that are waiting for a stream to become available (either
      // because we are still handshaking, or we reached the stream limit).
      std::queue<std::weak_ptr<uvw::TCPHandle>> pending_incoming;

      ~ClientTunnel();
    };

    // pseudo-port -> Client instance (the "port" is used to route incoming quic packets to the
    // right quic endpoint); pseudo-ports start at 1.
    std::map<uint16_t, ClientTunnel> client_tunnels_;

    uint16_t next_pseudo_port_ = 0;
    // bool pport_wrapped_ = false;

    bool
    continue_connecting(
        uint16_t pseudo_port, bool step_success, std::string_view step_name, std::string_view addr);

    void
    make_client(const SockAddr& remote, std::pair<const uint16_t, ClientTunnel>& row);

    void
    flush_pending_incoming(ClientTunnel& ct);

    // Server instance; this listens on pseudo-port 0 (if it listens).  This is automatically
    // instantiated the first time `listen()` is called; if not instantiated we simply drop any
    // inbound client-to-server quic packets.
    std::unique_ptr<Server> server_;

    void
    make_server();

    // Called when a new during connection handshaking once we have the established transport
    // parameters (which include the port) if this is an incoming connection (and this endpoint is a
    // server).  This checks handlers to see whether the stream is allowed and, if so, returns a
    // SockAddr containing the IP/port the tunnel should map to.  Returns nullopt if the connection
    // should be rejected.
    std::optional<SockAddr>
    allow_connection(std::string_view lokinet_addr, uint16_t port);

    // Incoming stream handlers
    std::map<int, ListenHandler> incoming_handlers_;
    int next_handler_id_ = 1;

    std::shared_ptr<uvw::Loop>
    get_loop();

    // Cleanup member
    std::shared_ptr<int> timer_keepalive_ = std::make_shared<int>(0);
  };

}  // namespace llarp::quic
```


-------------------------------

Updated on 2023-03-06 at 20:36:34 +0000
