2012-09-15 08:56:18 +02:00
|
|
|
#include <getopt.h> // for getopt_long
|
|
|
|
#include <stdlib.h> // for atoi
|
|
|
|
|
2012-09-10 01:10:20 +02:00
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <boost/asio.hpp>
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
#include <boost/thread/thread.hpp>
|
|
|
|
|
|
|
|
#define OPENVPN_CORE_API_VISIBILITY_HIDDEN // don't export core symbols
|
|
|
|
|
2012-09-12 01:51:37 +02:00
|
|
|
#include <openvpn/common/exception.hpp>
|
2012-09-15 08:56:18 +02:00
|
|
|
#include <openvpn/common/file.hpp>
|
|
|
|
#include <openvpn/time/timestr.hpp>
|
2012-09-12 01:51:37 +02:00
|
|
|
|
2012-09-10 01:10:20 +02:00
|
|
|
#include <client/ovpncli.cpp>
|
|
|
|
|
2012-09-15 08:56:18 +02:00
|
|
|
using namespace openvpn;
|
2012-09-10 01:10:20 +02:00
|
|
|
|
2012-09-15 08:56:18 +02:00
|
|
|
class Client : public ClientAPI::OpenVPNClient
|
2012-09-10 01:10:20 +02:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
virtual bool socket_protect(int socket)
|
|
|
|
{
|
|
|
|
std::cout << "*** socket_protect " << socket << std::endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-15 08:56:18 +02:00
|
|
|
virtual void event(const ClientAPI::Event& ev)
|
2012-09-10 01:10:20 +02:00
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << date_time() << " EVENT: " << ev.name;
|
|
|
|
if (!ev.info.empty())
|
|
|
|
std::cout << ' ' << ev.info;
|
|
|
|
if (ev.error)
|
|
|
|
std::cout << " [ERR]";
|
|
|
|
std::cout << std::endl << std::flush;
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:56:18 +02:00
|
|
|
virtual void log(const ClientAPI::LogInfo& log)
|
2012-09-10 01:10:20 +02:00
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << date_time() << ' ' << log.text << std::flush;
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|
|
|
|
|
2012-09-15 08:56:18 +02:00
|
|
|
virtual void external_pki_cert_request(ClientAPI::ExternalPKICertRequest& certreq)
|
2012-09-10 01:10:20 +02:00
|
|
|
{
|
|
|
|
std::cout << "*** external_pki_cert_request" << std::endl;
|
|
|
|
certreq.error = true;
|
|
|
|
certreq.errorText = "external_pki_cert_request not implemented";
|
|
|
|
}
|
|
|
|
|
2012-09-15 08:56:18 +02:00
|
|
|
virtual void external_pki_sign_request(ClientAPI::ExternalPKISignRequest& signreq)
|
2012-09-10 01:10:20 +02:00
|
|
|
{
|
|
|
|
std::cout << "*** external_pki_sign_request" << std::endl;
|
|
|
|
signreq.error = true;
|
|
|
|
signreq.errorText = "external_pki_sign_request not implemented";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Client *the_client = NULL;
|
|
|
|
|
|
|
|
void worker_thread()
|
|
|
|
{
|
|
|
|
boost::asio::detail::signal_blocker signal_blocker; // signals should be handled by parent thread
|
|
|
|
try {
|
|
|
|
std::cout << "Thread starting..." << std::endl;
|
2012-09-15 08:56:18 +02:00
|
|
|
ClientAPI::Status connect_status = the_client->connect();
|
2012-09-10 01:10:20 +02:00
|
|
|
if (connect_status.error)
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "connect error: " << connect_status.message << std::endl;
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "Connect thread exception: " << e.what() << std::endl;
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|
|
|
|
std::cout << "Thread finished" << std::endl;
|
|
|
|
}
|
|
|
|
|
2012-09-12 01:51:37 +02:00
|
|
|
void handler(int signum)
|
2012-09-10 01:10:20 +02:00
|
|
|
{
|
2012-09-12 01:51:37 +02:00
|
|
|
switch (signum)
|
|
|
|
{
|
|
|
|
case SIGTERM:
|
|
|
|
case SIGINT:
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "received stop signal " << signum << std::endl;
|
2012-09-12 01:51:37 +02:00
|
|
|
if (the_client)
|
|
|
|
the_client->stop();
|
|
|
|
break;
|
|
|
|
case SIGHUP:
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "received reconnect signal " << signum << std::endl;
|
2012-09-12 01:51:37 +02:00
|
|
|
if (the_client)
|
|
|
|
the_client->reconnect(2);
|
|
|
|
break;
|
|
|
|
default:
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "received unknown signal " << signum << std::endl;
|
2012-09-12 01:51:37 +02:00
|
|
|
break;
|
|
|
|
}
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
static const struct option longopts[] = {
|
|
|
|
{ "username", required_argument, NULL, 'u' },
|
|
|
|
{ "password", required_argument, NULL, 'p' },
|
|
|
|
{ "proto", required_argument, NULL, 'P' },
|
|
|
|
{ "server", required_argument, NULL, 's' },
|
|
|
|
{ "timeout", required_argument, NULL, 't' },
|
|
|
|
{ "compress", required_argument, NULL, 'c' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
2012-09-10 01:10:20 +02:00
|
|
|
try {
|
|
|
|
if (argc >= 2)
|
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
std::string username;
|
|
|
|
std::string password;
|
|
|
|
std::string proto;
|
|
|
|
std::string server;
|
|
|
|
int timeout = 0;
|
|
|
|
std::string compress;
|
|
|
|
int ch;
|
|
|
|
|
|
|
|
while ((ch = getopt_long(argc, argv, "u:p:P:s:t:c:", longopts, NULL)) != -1)
|
|
|
|
{
|
|
|
|
switch (ch)
|
|
|
|
{
|
|
|
|
case 'u':
|
|
|
|
username = optarg;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
password = optarg;
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
proto = optarg;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
server = optarg;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
timeout = atoi(optarg);
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
compress = optarg;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto usage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argc -= optind;
|
|
|
|
argv += optind;
|
|
|
|
|
2012-09-11 10:45:27 +02:00
|
|
|
Client::init_process();
|
2012-09-10 01:10:20 +02:00
|
|
|
Client client;
|
2012-09-15 08:56:18 +02:00
|
|
|
ClientAPI::Config config;
|
|
|
|
if (argc != 1)
|
|
|
|
goto usage;
|
|
|
|
config.content = read_text(argv[0]);
|
|
|
|
config.serverOverride = server;
|
|
|
|
config.protoOverride = proto;
|
|
|
|
config.connTimeout = timeout;
|
|
|
|
config.compressionMode = compress;
|
|
|
|
ClientAPI::EvalConfig eval = client.eval_config(config);
|
2012-09-10 01:10:20 +02:00
|
|
|
if (eval.error)
|
2012-09-12 01:51:37 +02:00
|
|
|
OPENVPN_THROW_EXCEPTION("eval config error: " << eval.message);
|
2012-09-10 01:10:20 +02:00
|
|
|
if (eval.autologin)
|
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
if (!username.empty() || !password.empty())
|
2012-09-10 01:10:20 +02:00
|
|
|
std::cout << "NOTE: creds were not needed" << std::endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
if (username.empty())
|
2012-09-12 01:51:37 +02:00
|
|
|
OPENVPN_THROW_EXCEPTION("need creds");
|
2012-09-15 08:56:18 +02:00
|
|
|
ClientAPI::ProvideCreds creds;
|
|
|
|
creds.username = username;
|
|
|
|
creds.password = password;
|
2012-09-10 01:10:20 +02:00
|
|
|
creds.replacePasswordWithSessionID = true;
|
2012-09-15 08:56:18 +02:00
|
|
|
ClientAPI::Status creds_status = client.provide_creds(creds);
|
2012-09-10 01:10:20 +02:00
|
|
|
if (creds_status.error)
|
2012-09-12 01:51:37 +02:00
|
|
|
OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "CONNECTING..." << std::endl;
|
|
|
|
|
|
|
|
// catch signals
|
2012-09-12 01:51:37 +02:00
|
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = handler;
|
|
|
|
sigemptyset(&sa.sa_mask);
|
2012-09-12 06:24:05 +02:00
|
|
|
sa.sa_flags = SA_RESTART; // restart functions if interrupted by handler
|
2012-09-12 01:51:37 +02:00
|
|
|
if (sigaction(SIGINT, &sa, NULL) == -1
|
|
|
|
|| sigaction(SIGTERM, &sa, NULL) == -1
|
|
|
|
|| sigaction(SIGHUP, &sa, NULL) == -1)
|
|
|
|
OPENVPN_THROW_EXCEPTION("error setting signal handler");
|
2012-09-10 01:10:20 +02:00
|
|
|
|
|
|
|
// start connect thread
|
|
|
|
the_client = &client;
|
|
|
|
boost::thread* thread = new boost::thread(boost::bind(&worker_thread));
|
|
|
|
|
|
|
|
// wait for connect thread to exit
|
|
|
|
thread->join();
|
|
|
|
the_client = NULL;
|
2012-09-15 08:56:18 +02:00
|
|
|
|
|
|
|
// print closing stats
|
|
|
|
{
|
|
|
|
const int n = client.stats_n();
|
|
|
|
std::vector<long long> stats = client.stats_bundle();
|
|
|
|
|
|
|
|
std::cout << "STATS:" << std::endl;
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
{
|
|
|
|
const long long value = stats[i];
|
|
|
|
if (value)
|
|
|
|
std::cout << " " << client.stats_name(i) << " : " << value << std::endl;
|
|
|
|
}
|
|
|
|
}
|
2012-09-10 01:10:20 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-15 08:56:18 +02:00
|
|
|
goto usage;
|
2012-09-10 01:10:20 +02:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
|
|
|
the_client = NULL;
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "Main thread exception: " << e.what() << std::endl;
|
2012-09-10 01:10:20 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2012-09-15 08:56:18 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
usage:
|
|
|
|
std::cout << "OpenVPN Client (ovpncli)" << std::endl;
|
|
|
|
std::cout << "usage: cli <config-file> [options]" << std::endl;
|
|
|
|
std::cout << "--username, -u : username" << std::endl;
|
|
|
|
std::cout << "--password, -p : password" << std::endl;
|
|
|
|
std::cout << "--proto, -P : protocol override (udp|tcp)" << std::endl;
|
2012-09-22 21:11:52 +02:00
|
|
|
std::cout << "--server, -s : server override" << std::endl;
|
2012-09-15 08:56:18 +02:00
|
|
|
std::cout << "--timeout, -t : timeout" << std::endl;
|
|
|
|
std::cout << "--compress, -c : compression mode (yes|no|asym)" << std::endl;
|
|
|
|
return 2;
|
2012-09-10 01:10:20 +02:00
|
|
|
}
|