mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/win/client/setupbase.hpp>
|
||||
#include <openvpn/tun/win/client/clientconfig.hpp>
|
||||
#include <openvpn/win/modname.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
class WintunClient : public TunClient
|
||||
{
|
||||
typedef RCPtr<WintunClient> Ptr;
|
||||
|
||||
public:
|
||||
WintunClient(openvpn_io::io_context& io_context_arg,
|
||||
ClientConfig* config_arg,
|
||||
TunClientParent& parent_arg)
|
||||
: io_context(io_context_arg),
|
||||
config(config_arg),
|
||||
parent(parent_arg),
|
||||
state(new TunProp::State()),
|
||||
frame(config_arg->frame)
|
||||
{
|
||||
}
|
||||
|
||||
// Inherited via TunClient
|
||||
void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override
|
||||
{
|
||||
halt = false;
|
||||
if (config->tun_persist)
|
||||
tun_persist = config->tun_persist; // long-term persistent
|
||||
else
|
||||
tun_persist.reset(new TunPersist(false, false, nullptr)); // short-term
|
||||
|
||||
try {
|
||||
|
||||
const IP::Addr server_addr = transcli.server_endpoint_addr();
|
||||
|
||||
// Check if persisted tun session matches properties of to-be-created session
|
||||
if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
|
||||
{
|
||||
state = tun_persist->state().state;
|
||||
ring_buffer = tun_persist->state().ring_buffer;
|
||||
OPENVPN_LOG("TunPersist: reused tun context");
|
||||
}
|
||||
else
|
||||
{
|
||||
// notify parent
|
||||
parent.tun_pre_tun_config();
|
||||
|
||||
// close old TAP handle if persisted
|
||||
tun_persist->close();
|
||||
|
||||
// parse pushed options
|
||||
TunBuilderCapture::Ptr po(new TunBuilderCapture());
|
||||
TunProp::configure_builder(po.get(),
|
||||
state.get(),
|
||||
config->stats.get(),
|
||||
server_addr,
|
||||
config->tun_prop,
|
||||
opt,
|
||||
nullptr,
|
||||
false);
|
||||
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
||||
|
||||
// create new tun setup object
|
||||
tun_setup = config->new_setup_obj(io_context);
|
||||
|
||||
ring_buffer.reset(new RingBuffer(io_context));
|
||||
|
||||
// open/config TAP
|
||||
HANDLE th;
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os]() { OPENVPN_LOG_STRING(os.str()); });
|
||||
th = tun_setup->establish(*po, Win::module_name(), config->stop, os, ring_buffer);
|
||||
}
|
||||
|
||||
// create ASIO wrapper for HANDLE
|
||||
TAPStream* ts = new TAPStream(io_context, th);
|
||||
|
||||
// persist tun settings state
|
||||
if (tun_persist->persist_tun_state(ts, { state, ring_buffer }))
|
||||
OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options());
|
||||
|
||||
// enable tun_setup destructor
|
||||
tun_persist->add_destructor(tun_setup);
|
||||
|
||||
// assert ownership over TAP device handle
|
||||
tun_setup->confirm();
|
||||
}
|
||||
|
||||
openvpn_io::post(io_context, [self=Ptr(this)](){
|
||||
self->read();
|
||||
});
|
||||
|
||||
parent.tun_connected(); // signal that we are connected
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stop();
|
||||
Error::Type err = Error::TUN_SETUP_FAILED;
|
||||
const ExceptionCode* ec = dynamic_cast<const ExceptionCode*>(&e);
|
||||
if (ec && ec->code_defined())
|
||||
err = ec->code();
|
||||
parent.tun_error(err, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
|
||||
tun_persist.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void set_disconnect() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool tun_send(BufferAllocated& buf) override
|
||||
{
|
||||
TUN_RING* receive_ring = ring_buffer->receive_ring();
|
||||
|
||||
ULONG head = receive_ring->head.load(std::memory_order_acquire);
|
||||
if (head > WINTUN_RING_CAPACITY)
|
||||
{
|
||||
if (head == 0xFFFFFFFF)
|
||||
parent.tun_error(Error::TUN_WRITE_ERROR, "invalid ring head/tail or bogus packet received");
|
||||
return false;
|
||||
}
|
||||
|
||||
ULONG tail = receive_ring->tail.load(std::memory_order_acquire);
|
||||
if (tail >= WINTUN_RING_CAPACITY)
|
||||
return false;
|
||||
|
||||
ULONG aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + buf.size());
|
||||
ULONG buf_space = wrap(head - tail - WINTUN_PACKET_ALIGN);
|
||||
if (aligned_packet_size > buf_space)
|
||||
{
|
||||
OPENVPN_LOG("ring is full");
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy packet size and data into ring
|
||||
TUN_PACKET* packet = (TUN_PACKET*)& receive_ring->data[tail];
|
||||
packet->size = buf.size();
|
||||
std::memcpy(packet->data, buf.data(), buf.size());
|
||||
|
||||
// move ring tail
|
||||
receive_ring->tail.store(wrap(tail + aligned_packet_size), std::memory_order_release);
|
||||
if (receive_ring->alertable.load(std::memory_order_acquire) != 0)
|
||||
SetEvent(ring_buffer->receive_ring_tail_moved());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string tun_name() const override
|
||||
{
|
||||
return "wintun";
|
||||
}
|
||||
|
||||
std::string vpn_ip4() const override
|
||||
{
|
||||
if (state->vpn_ip4_addr.specified())
|
||||
return state->vpn_ip4_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string vpn_ip6() const override
|
||||
{
|
||||
if (state->vpn_ip6_addr.specified())
|
||||
return state->vpn_ip6_addr.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string vpn_gw4() const override
|
||||
{
|
||||
if (state->vpn_ip4_gw.specified())
|
||||
return state->vpn_ip4_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string vpn_gw6() const override
|
||||
{
|
||||
if (state->vpn_ip6_gw.specified())
|
||||
return state->vpn_ip6_gw.to_string();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
void read()
|
||||
{
|
||||
TUN_RING* send_ring = ring_buffer->send_ring();
|
||||
|
||||
if (halt)
|
||||
return;
|
||||
|
||||
ULONG head = send_ring->head.load(std::memory_order_acquire);
|
||||
if (head >= WINTUN_RING_CAPACITY)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "ring head exceeds ring capacity");
|
||||
return;
|
||||
}
|
||||
|
||||
ULONG tail = send_ring->tail.load(std::memory_order_acquire);
|
||||
if (tail >= WINTUN_RING_CAPACITY)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "ring tail exceeds ring capacity");
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// tail has moved?
|
||||
if (head == tail)
|
||||
{
|
||||
ring_buffer->send_tail_moved_asio_event().async_wait([self = Ptr(this)](const openvpn_io::error_code& error) {
|
||||
if (!error)
|
||||
self->read();
|
||||
else
|
||||
{
|
||||
if (!self->halt)
|
||||
self->parent.tun_error(Error::TUN_ERROR, "error waiting on ring send tail moved");
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// read buffer content
|
||||
ULONG content_len = wrap(tail - head);
|
||||
if (content_len < sizeof(TUN_PACKET_HEADER))
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "incomplete packet header in send ring");
|
||||
return;
|
||||
}
|
||||
|
||||
TUN_PACKET* packet = (TUN_PACKET *)&send_ring->data[head];
|
||||
if (packet->size > WINTUN_MAX_PACKET_SIZE)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "packet too big in send ring");
|
||||
return;
|
||||
}
|
||||
|
||||
ULONG aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + packet->size);
|
||||
if (aligned_packet_size > content_len)
|
||||
{
|
||||
parent.tun_error(Error::TUN_ERROR, "incomplete packet in send ring");
|
||||
return;
|
||||
}
|
||||
|
||||
frame->prepare(Frame::READ_TUN, buf);
|
||||
|
||||
buf.write(packet->data, packet->size);
|
||||
|
||||
head = wrap(head + aligned_packet_size);
|
||||
send_ring->head.store(head, std::memory_order_release);
|
||||
|
||||
parent.tun_recv(buf);
|
||||
|
||||
if (halt)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct TUN_PACKET_HEADER
|
||||
{
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct TUN_PACKET
|
||||
{
|
||||
uint32_t size;
|
||||
UCHAR data[WINTUN_MAX_PACKET_SIZE];
|
||||
};
|
||||
|
||||
ULONG packet_align(ULONG size)
|
||||
{
|
||||
return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1);
|
||||
}
|
||||
|
||||
ULONG wrap(ULONG value)
|
||||
{
|
||||
return value & (WINTUN_RING_CAPACITY - 1);
|
||||
}
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
TunPersist::Ptr tun_persist; // contains the TAP device HANDLE
|
||||
ClientConfig::Ptr config;
|
||||
TunClientParent& parent;
|
||||
TunProp::State::Ptr state;
|
||||
TunWin::SetupBase::Ptr tun_setup;
|
||||
|
||||
TUN_RING* receive_ring = nullptr;
|
||||
TUN_RING* send_ring = nullptr;
|
||||
|
||||
BufferAllocated buf;
|
||||
|
||||
Frame::Ptr frame;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
ScopedHANDLE driver_handle;
|
||||
|
||||
RingBuffer::Ptr ring_buffer;
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user