text: //    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-2017 OpenVPN Inc.
  //
  //    This program is free software: you can redistribute it and/or modify
  //    it under the terms of the GNU Affero General Public License Version 3
  //    as published by the Free Software Foundation.
  //
  //    This program is distributed in the hope that it will be useful,
  //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //    GNU Affero General Public License for more details.
  //
  //    You should have received a copy of the GNU Affero General Public License
  //    along with this program in the COPYING file.
  //    If not, see <http://www.gnu.org/licenses/>.
  
  // Create a special transport factory that persists an existing
  // transport client.  This is used to preserve the transport
  // socket when other client components are restarted after
  // a RELAY message is received from the server.
  
  #ifndef OPENVPN_TRANSPORT_CLIENT_RELAY_H
  #define OPENVPN_TRANSPORT_CLIENT_RELAY_H
  
  #include <memory>
  
  #include <openvpn/transport/client/transbase.hpp>
  
  namespace openvpn {
    class TransportRelayFactory : public TransportClientFactory
    {
    public:
      TransportRelayFactory(openvpn_io::io_context& io_context,
  			  TransportClient::Ptr transport,
  			  TransportClientParent* old_parent)
        : io_context_(io_context),
  	transport_(std::move(transport)),
  	null_parent_(new NullParent(old_parent))
      {
        // Temporarily point transport to our null parent
        transport_->transport_reparent(null_parent_.get());
      }
  
      class TransportClientNull : public TransportClient
      {
      public:
        TransportClientNull(TransportClient* old)
  	: endpoint_(old->server_endpoint_addr()),
  	  protocol_(old->transport_protocol())
        {
  	old->server_endpoint_info(host_, port_, proto_, ip_addr_);
        }
  
      private:
        virtual void transport_start() {}
        virtual void stop() {}
        virtual bool transport_send_const(const Buffer& buf) { return false; }
        virtual bool transport_send(BufferAllocated& buf) { return false; }
        virtual bool transport_send_queue_empty() { return false; }
        virtual bool transport_has_send_queue() { return false; }
        virtual unsigned int transport_send_queue_size() { return 0; }
        virtual void transport_stop_requeueing() { }
        virtual void reset_align_adjust(const size_t align_adjust) {}
        virtual void transport_reparent(TransportClientParent* parent) {}
  
        virtual IP::Addr server_endpoint_addr() const
        {
  	return endpoint_;
        }
  
        virtual Protocol transport_protocol() const
        {
  	return protocol_;
        }
  
        virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
        {
  	host = host_;
  	port = port_;
  	proto = proto_;
  	ip_addr = ip_addr_;
        }
  
        IP::Addr endpoint_;
        Protocol protocol_;
        std::string host_;
        std::string port_;
        std::string proto_;
        std::string ip_addr_;
      };
  
    private:
      class NullParent : public TransportClientParent
      {
      public:
        NullParent(TransportClientParent* old_parent)
  	: is_openvpn_protocol(old_parent->transport_is_openvpn_protocol())
        {
        }
  
      private:
        virtual void transport_recv(BufferAllocated& buf) {}
        virtual void transport_needs_send() {}
  
        virtual void transport_error(const Error::Type fatal_err, const std::string& err_text)
        {
  	OPENVPN_LOG("TransportRelayFactory: Transport Error in null parent: " << Error::name(fatal_err) << " : " << err_text);
        }
  
        virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text)
        {
  	OPENVPN_LOG("TransportRelayFactory: Proxy Error in null parent: " << Error::name(fatal_err) << " : " << err_text);
        }
  
        virtual void ip_hole_punch(const IP::Addr& addr) {}
  
        // Return true if we are transporting OpenVPN protocol
        virtual bool transport_is_openvpn_protocol() { return is_openvpn_protocol; }
  
        // progress notifications
        virtual void transport_pre_resolve() {}
        virtual void transport_wait_proxy() {}
        virtual void transport_wait() {}
        virtual void transport_connecting() {}
  
        // Return true if keepalive parameter(s) are enabled.
        virtual bool is_keepalive_enabled() const { return false; }
  
        // Disable keepalive for rest of session, but fetch
        // the keepalive parameters (in seconds).
        virtual void disable_keepalive(unsigned int& keepalive_ping, unsigned int& keepalive_timeout)
        {
  	keepalive_ping = 0;
  	keepalive_timeout = 0;
        }
  
        bool is_openvpn_protocol;
      };
  
      virtual TransportClient::Ptr new_transport_client_obj(openvpn_io::io_context& io_context,
  							  TransportClientParent* parent) override
      {
        // io_context MUST stay consistent
        if (&io_context != &io_context_)
  	throw Exception("TransportRelayFactory: inconsistent io_context");
  
        transport_->transport_reparent(parent);
        return transport_;
      }
  
      virtual bool is_relay() override
      {
        return true;
      }
  
      openvpn_io::io_context& io_context_;                        // only used to verify consistency
      TransportClient::Ptr transport_;                      // the persisted transport
      std::unique_ptr<TransportClientParent> null_parent_;  // placeholder for TransportClient parent before reparenting
    };
  }
  
  #endif
  