File: asioboundsock.hpp

package info (click to toggle)
openvpn3-client 25%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,276 kB
  • sloc: cpp: 190,085; python: 7,218; ansic: 1,866; sh: 1,361; java: 402; lisp: 81; makefile: 17
file content (143 lines) | stat: -rw-r--r-- 3,591 bytes parent folder | download
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