1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
// Asio TCP socket that can be configured so that open() method
// always prebinds the socket to a given local address. Useful
// for TCP clients.
#ifndef OPENVPN_ASIO_ASIOBOUNDSOCK_H
#define OPENVPN_ASIO_ASIOBOUNDSOCK_H
#include <openvpn/io/io.hpp>
#include <openvpn/addr/ip.hpp>
#include <openvpn/common/to_string.hpp>
namespace openvpn::AsioBoundSocket {
typedef openvpn_io::basic_stream_socket<openvpn_io::ip::tcp> SocketBase;
class Socket : public SocketBase
{
public:
explicit Socket(openvpn_io::io_context &io_context)
: SocketBase(io_context)
{
}
// May be called twice with IPv4 and IPv6 address.
// If port 0, kernel will dynamically allocate free port.
void bind_local(const IP::Addr &addr, const unsigned short port = 0)
{
switch (addr.version())
{
case IP::Addr::V4:
v4.bind_local(addr.to_ipv4(), port);
break;
case IP::Addr::V6:
v6.bind_local(addr.to_ipv6(), port);
break;
default:
return;
}
}
std::string to_string() const
{
std::string ret;
ret.reserve(64);
if (v4.defined())
{
ret += "local4=";
ret += v4.to_string();
}
if (v6.defined())
{
if (!ret.empty())
ret += ' ';
ret += "local6=";
ret += v6.to_string();
}
try
{
const std::string re = openvpn::to_string(remote_endpoint());
if (!ret.empty())
ret += ' ';
ret += "remote=";
ret += re;
}
catch (const std::exception &e)
{
}
return ret;
}
protected:
template <typename IP_ADDR>
class Proto
{
public:
Proto()
{
local_.zero();
port_ = 0;
}
bool defined() const
{
return local_.specified();
}
void bind_local(const IP_ADDR &local, const unsigned short port)
{
local_ = local;
port_ = port;
}
template <typename PARENT>
void post_open(PARENT *parent, openvpn_io::error_code &ec) const
{
if (defined())
{
parent->set_option(openvpn_io::socket_base::reuse_address(true), ec);
if (!ec)
parent->bind(openvpn_io::ip::tcp::endpoint(local_.to_asio(), port_), ec);
}
}
std::string to_string() const
{
return '[' + local_.to_string() + "]:" + std::to_string(port_);
}
private:
IP_ADDR local_;
unsigned short port_;
};
virtual void async_connect_post_open(const protocol_type &protocol, openvpn_io::error_code &ec) override
{
if (protocol == openvpn_io::ip::tcp::v4())
v4.post_open(this, ec);
else if (protocol == openvpn_io::ip::tcp::v6())
v6.post_open(this, ec);
}
private:
Proto<IPv4::Addr> v4;
Proto<IPv6::Addr> v6;
};
} // namespace openvpn::AsioBoundSocket
#endif
|