OpenVPN / openvpn3

OpenVPN 3 is a C++ class library that implements the functionality of an OpenVPN client, and is protocol-compatible with the OpenVPN 2.x branch.
https://openvpn.net
Other
977 stars 386 forks source link

Creating a separate header and source file #66

Closed ninigij closed 4 years ago

ninigij commented 5 years ago

Hi, I have a problem that has been jeopardizing the state of my project. I'm trying to create a class that inherits from both QObject (this is a Qt project) and OpenVPNClient. The problem is that I get bombarded with "multiple redefinition of" errors at link-time. I understand that this issue is similar, but I'm having trouble understanding it. How exactly do I stop my class from exporting any OpenVPN3 symbols? My application specifically requires that the class declaration and implementation be in different files. I've tried forward declaring the OpenVPN-related classes I need to use in my header file, but since my class inherits from OpeNVPNClient (defined in ovpncli.cpp) I can't omit that inclusion.

#ifndef OPENVPN_H
#define OPENVPN_H

#include <stdlib.h> // for atoi

#include <string>
#include <iostream>
#include <thread>
#include <memory>
#include <mutex>

#include "vpnstatus.h"
#include <algorithm>
#include <QObject>

namespace openvpn {
    namespace ClientAPI {
        class OpenVPNClient;
    }
    #if defined(USE_MBEDTLS)
        namespace MbedTLSPKI {
            class PKContext;
        }
    #endif
}

using namespace openvpn;

class Client : public QObject, public ClientAPI::OpenVPNClient {
    Q_OBJECT
public:
    enum ClockTickAction {
        CT_UNDEF,
        CT_STOP,
        CT_RECONNECT,
        CT_PAUSE,
        CT_RESUME,
        CT_STATS,
    };

    /**
     * @brief Check to see whether or not there is a dynamic challenge
     * @return Returns true if we have a dynamic challenge cookie present
     */
    bool is_dynamic_challenge() const;

    std::string dynamic_challenge_cookie();

    std::string epki_ca;
    std::string epki_cert;
#if defined(USE_MBEDTLS)
    MbedTLSPKI::PKContext epki_ctx; // external PKI context
#endif

    void set_clock_tick_action(const ClockTickAction action);

    void get_stats();

private:
    virtual bool socket_protect(int socket) override;

    virtual void event(const ClientAPI::Event &ev) override;

    virtual void log(const ClientAPI::LogInfo &log) override;

    virtual void clock_tick() override;

    virtual void external_pki_cert_request(ClientAPI::ExternalPKICertRequest &certreq) override;

    virtual void external_pki_sign_request(ClientAPI::ExternalPKISignRequest &signreq) override;

    static int rng_callback(void *arg, unsigned char *data, size_t len);

    virtual bool pause_on_connection_timeout() override;

    std::mutex log_mutex;
    std::string dc_cookie;
    RandomAPI::Ptr rng;      // random data source for epki
    volatile ClockTickAction clock_tick_action = CT_UNDEF;

#ifdef OPENVPN_REMOTE_OVERRIDE
    std::string remote_override_cmd;
#endif
};

static void start_thread(Client& client);

static Client *the_client = nullptr;

static void worker_thread();

static std::string read_profile(const char *fn, const std::string *profile_content);

#endif  // OPENVPN_H
schwabe commented 5 years ago

Basically include OpenVPN3 headers only in your cpp and not in your hpp file. You might to split your class into an abstract class and implementation variant to be able to achieve this.

ninigij commented 5 years ago

I understand that I must include OpenVPN3 headers only in my cpp file, but that's impossible (as far as I know) to do as I've stated. Eventually, one header file must include ovpncli.cpp since it has to inherit from OpenVPNClient. Here's what I have right now:

#ifndef VPNCLIENTINF_H
#define VPNCLIENTINF_H

#include <client/ovpncli.cpp>

class VPNClientInf : public openvpn::ClientAPI::OpenVPNClient {

};

#endif // VPNCLIENTINF_H

openvpn.h includes this file and inherits from VPNClientInf. It does not include any additional OpenVPN header files. openvpn.cpp includes openvpn.h as well as the rest of the OpenVPN header files. This is still causing a multiple definition error for me. If you could provide an example, this would be immensely helpful.

dsommers commented 5 years ago

Have a look at core-client.hpp in the openvpn3-linux client

The client object from the OpenVPN 3 Core Library is provided in core-client.hpp which is used by the rest of the client code in openvpn3-service-client.cpp.

dsommers commented 5 years ago

The main issue you have is this line:

 #include <client/ovpncli.cpp>

This needs to only be included once.

ninigij commented 5 years ago

@dsommers From what I can tell, openvpn3-linux-client does not demonstrate providing a separate header and source file and then going on to include the header file both in the source file and in another file. I understand that by me having ovpncli.cpp included in openvpn.h, I'm implicitly including it multiple times when I include openvpn.h more than once in my project, thus giving me a multiple definition error.

I'm wondering precisely how I can stop my openvpn.h file from exporting the symbols from ovpncli.cpp multiple times and avoid this problem. Like I said before, it's impossible for me to not have ovpncli.cpp in openvpn.h