0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 19:52:15 +02:00
openvpn3/openvpn/transport/tcplinkcommon.hpp
Charlie Vigue ef8da98bd4 Buffer: Prepare to decouple allocated buffer / RC
Rename BufferAllocated --> BufferAllocatedRc

Buffer: split RC from BufferAllocated
Also make changes as needed where BufferAllocated is used

Buffer: Split allocation flags into own struct
Leaving flags in template causes each alias to have identical flags
by different names, which requires each type to pointlessly use
the nested name.

Make RC: Clean up headers buffer.hpp, make_rc.hpp

Signed-off-by: Charlie Vigue <charlie.vigue@openvpn.com>
2024-09-11 13:23:28 +00:00

479 lines
13 KiB
C++

// Copyright (C) 2012-2022 OpenVPN Inc.
// Base class for TCP link objects.
#ifndef OPENVPN_TRANSPORT_COMMONLINK_H
#define OPENVPN_TRANSPORT_COMMONLINK_H
#include <deque>
#include <utility> // for std::move
#include <memory>
#include <openvpn/io/io.hpp>
#include <openvpn/common/size.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/socktypes.hpp>
#include <openvpn/error/excode.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/transport/tcplinkbase.hpp>
#include <openvpn/transport/pktstream.hpp>
#include <openvpn/transport/mutate.hpp>
#ifdef OPENVPN_GREMLIN
#include <openvpn/transport/gremlin.hpp>
#endif
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1
#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_TCPLINK_ERROR(x)
#endif
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 3
#define OPENVPN_LOG_TCPLINK_VERBOSE(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_TCPLINK_VERBOSE(x)
#endif
namespace openvpn::TCPTransport {
template <typename Protocol,
typename ReadHandler,
bool RAW_MODE_ONLY>
class LinkCommon : public LinkBase
{
typedef std::deque<BufferPtr> Queue;
public:
typedef RCPtr<LinkCommon<Protocol, ReadHandler, RAW_MODE_ONLY>> Ptr;
typedef Protocol protocol;
typedef PacketStream<std::uint16_t> OpenVPNPacketStream;
// In raw mode, data is sent and received without any special encapsulation.
// In non-raw mode, data is packetized by prepending a 16-bit length word
// onto each packet. The OpenVPN and DNS protocols run in non-raw mode,
// while other TCP protocols such as HTTP or HTTPS would run in raw mode.
// This method is a no-op if RAW_MODE_ONLY is true.
void set_raw_mode(const bool mode)
{
set_raw_mode_read(mode);
set_raw_mode_write(mode);
}
void set_raw_mode_read(const bool mode)
{
if (RAW_MODE_ONLY)
raw_mode_read = true;
else
raw_mode_read = mode;
}
void set_raw_mode_write(const bool mode)
{
if (RAW_MODE_ONLY)
raw_mode_write = true;
else
raw_mode_write = mode;
}
void set_mutate(const TransportMutateStream::Ptr &mutate_arg)
{
mutate = mutate_arg;
}
bool send_queue_empty() const
{
return send_queue_size() == 0;
}
void inject(const Buffer &src)
{
const size_t size = src.size();
OPENVPN_LOG_TCPLINK_VERBOSE("TCP inject size=" << size);
if (size && !RAW_MODE_ONLY)
{
BufferAllocated buf;
frame_context.prepare(buf);
buf.write(src.c_data(), size);
BufferAllocated pkt;
put_pktstream(buf, pkt);
}
}
void start()
{
if (!halt)
queue_recv(nullptr);
}
void stop()
{
halt = true;
#ifdef OPENVPN_GREMLIN
if (gremlin)
gremlin->stop();
#endif
}
void reset_align_adjust(const size_t align_adjust)
{
frame_context.reset_align_adjust(align_adjust + (is_raw_mode() ? 0 : 2));
}
size_t send_queue_size() const
{
return queue.size()
#ifdef OPENVPN_GREMLIN
+ (gremlin ? gremlin->send_size() : 0)
#endif
;
}
bool send(BufferAllocated &b)
{
if (halt)
{
return false;
}
if (send_queue_max_size && send_queue_size() >= send_queue_max_size)
{
stats->error(Error::TCP_OVERFLOW);
read_handler->tcp_error_handler("TCP_OVERFLOW");
stop();
return false;
}
BufferPtr buf;
if (!free_list.empty())
{
buf = free_list.front();
free_list.pop_front();
}
else
{
buf = BufferAllocatedRc::Create();
}
buf->swap(b);
if (!is_raw_mode_write())
{
OpenVPNPacketStream::prepend_size(*buf);
}
if (mutate)
{
mutate->pre_send(*buf);
}
#ifdef OPENVPN_GREMLIN
if (gremlin)
{
gremlin_queue_send_buffer(buf);
}
else
#endif
{
from_app_send_buffer(buf);
}
return true;
}
void queue_recv(PacketFrom *tcpfrom)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::queue_recv");
if (!tcpfrom)
tcpfrom = new PacketFrom();
frame_context.prepare(tcpfrom->buf);
socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf),
[self = Ptr(this), tcpfrom = PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code &error, const size_t bytes_recvd) mutable
{
OPENVPN_ASYNC_HANDLER;
try
{
self->handle_recv(std::move(tcpfrom), error, bytes_recvd);
}
catch (const std::exception &e)
{
Error::Type err = Error::TCP_SIZE_ERROR;
const char *msg = "TCP_SIZE_ERROR";
// if exception is an ExceptionCode, translate the code
// to return status string
{
const ExceptionCode *ec = dynamic_cast<const ExceptionCode *>(&e);
if (ec && ec->code_defined())
{
err = ec->code();
msg = ec->what();
}
}
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what());
self->stats->error(err);
self->read_handler->tcp_error_handler(msg);
self->stop();
}
});
}
protected:
LinkCommon(ReadHandler read_handler_arg,
typename Protocol::socket &socket_arg,
const size_t send_queue_max_size_arg, // 0 to disable
const size_t free_list_max_size_arg,
const Frame::Context &frame_context_arg,
const SessionStats::Ptr &stats_arg)
: socket(socket_arg),
read_handler(read_handler_arg),
frame_context(frame_context_arg),
stats(stats_arg),
send_queue_max_size(send_queue_max_size_arg),
free_list_max_size(free_list_max_size_arg)
{
set_raw_mode(false);
}
#ifdef OPENVPN_GREMLIN
void gremlin_config(const Gremlin::Config::Ptr &config)
{
if (config)
gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, true));
}
#endif
bool is_raw_mode() const
{
return is_raw_mode_read() && is_raw_mode_write();
}
bool is_raw_mode_read() const
{
if (RAW_MODE_ONLY)
return true;
else
return raw_mode_read;
}
bool is_raw_mode_write() const
{
if (RAW_MODE_ONLY)
return true;
else
return raw_mode_write;
}
LinkCommon()
{
stop();
}
void queue_send_buffer(BufferPtr &buf)
{
queue.push_back(std::move(buf));
if (queue.size() == 1) // send operation not currently active?
queue_send();
}
void queue_send()
{
BufferAllocated &buf = *queue.front();
socket.async_send(buf.const_buffer_clamp(),
[self = Ptr(this)](const openvpn_io::error_code &error, const size_t bytes_sent)
{
OPENVPN_ASYNC_HANDLER;
self->handle_send(error, bytes_sent);
});
}
void handle_send(const openvpn_io::error_code &error, const size_t bytes_sent)
{
if (!halt)
{
if (!error)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TLS-TCP send raw=" << raw_mode_write << " size=" << bytes_sent);
stats->inc_stat(SessionStats::BYTES_OUT, bytes_sent);
stats->inc_stat(SessionStats::PACKETS_OUT, 1);
BufferPtr buf = queue.front();
if (bytes_sent == buf->size())
{
queue.pop_front();
if (free_list.size() < free_list_max_size)
{
buf->reset_content();
free_list.push_back(std::move(buf)); // recycle the buffer for later use
}
}
else if (bytes_sent < buf->size())
buf->advance(bytes_sent);
else
{
stats->error(Error::TCP_OVERFLOW);
read_handler->tcp_error_handler("TCP_INTERNAL_ERROR"); // error sent more bytes than we asked for
stop();
return;
}
}
else
{
OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP send error: " << error.message());
stats->error(Error::NETWORK_SEND_ERROR);
read_handler->tcp_error_handler("NETWORK_SEND_ERROR");
stop();
return;
}
if (!queue.empty())
queue_send();
else
tcp_write_queue_needs_send();
}
}
bool process_recv_buffer(BufferAllocated &buf)
{
bool requeue = true;
OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::process_recv_buffer: size=" << buf.size());
if (!is_raw_mode_read())
{
try
{
BufferAllocated pkt;
requeue = put_pktstream(buf, pkt);
if (!buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer
buf.move(pkt);
}
catch ([[maybe_unused]] const std::exception &e)
{
OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP packet extract error: " << e.what());
stats->error(Error::TCP_SIZE_ERROR);
read_handler->tcp_error_handler("TCP_SIZE_ERROR");
stop();
return false;
}
}
else
{
if (mutate)
{
mutate->post_recv(buf);
}
#ifdef OPENVPN_GREMLIN
if (gremlin)
{
requeue = gremlin_recv(buf);
}
else
#endif
{
requeue = read_handler->tcp_read_handler(buf);
}
}
return requeue;
}
void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code &error, const size_t bytes_recvd)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TCPLink::handle_recv: " << error.message());
if (!halt)
{
if (!error)
{
recv_buffer(pfp, bytes_recvd);
}
else if (error == openvpn_io::error::eof)
{
OPENVPN_LOG_TCPLINK_ERROR("TCP recv EOF");
read_handler->tcp_eof_handler();
}
else
{
OPENVPN_LOG_TCPLINK_ERROR("TCP recv error: " << error.message());
stats->error(Error::NETWORK_RECV_ERROR);
read_handler->tcp_error_handler("NETWORK_RECV_ERROR");
stop();
}
}
}
bool put_pktstream(BufferAllocated &buf, BufferAllocated &pkt)
{
bool requeue = true;
stats->inc_stat(SessionStats::BYTES_IN, buf.size());
stats->inc_stat(SessionStats::PACKETS_IN, 1);
if (mutate)
mutate->post_recv(buf);
while (buf.size())
{
pktstream.put(buf, frame_context);
if (pktstream.ready())
{
pktstream.get(pkt);
#ifdef OPENVPN_GREMLIN
if (gremlin)
requeue = gremlin_recv(pkt);
else
#endif
requeue = read_handler->tcp_read_handler(pkt);
}
}
return requeue;
}
#ifdef OPENVPN_GREMLIN
void gremlin_queue_send_buffer(BufferPtr &buf)
{
gremlin->send_queue([self = Ptr(this), buf = std::move(buf)]() mutable
{
if (!self->halt)
{
self->queue_send_buffer(buf);
} });
}
bool gremlin_recv(BufferAllocated &buf)
{
gremlin->recv_queue([self = Ptr(this), buf = std::move(buf)]() mutable
{
if (!self->halt)
{
const bool requeue = self->read_handler->tcp_read_handler(buf);
if (requeue)
self->queue_recv(nullptr);
} });
return false;
}
#endif
void tcp_write_queue_needs_send()
{
read_handler->tcp_write_queue_needs_send();
}
typename Protocol::socket &socket;
ReadHandler read_handler;
Frame::Context frame_context;
SessionStats::Ptr stats;
const size_t send_queue_max_size;
const size_t free_list_max_size;
Queue queue; // send queue
Queue free_list; // recycled free buffers for send queue
OpenVPNPacketStream pktstream;
TransportMutateStream::Ptr mutate;
bool raw_mode_read;
bool raw_mode_write;
bool halt = false;
#ifdef OPENVPN_GREMLIN
std::unique_ptr<Gremlin::SendRecvQueue> gremlin;
#endif
private:
virtual void recv_buffer(PacketFrom::SPtr &pfp, const size_t bytes_recvd) = 0;
virtual void from_app_send_buffer(BufferPtr &buf) = 0;
};
} // namespace openvpn::TCPTransport
#endif