mirror of
https://github.com/amnezia-vpn/openvpn3.git
synced 2026-05-17 00:16:12 +03:00
agent mac: move from common
Windows agent has been moved from common to core, so for consistency move mac agent too. Since agent and agent-enabled client depend on jsoncpp, also move jsoncpp build scripts. Signed-off-by: Lev Stipakov <lev@openvpn.net>
This commit is contained in:
committed by
David Sommerseth
parent
0ad1bfecc0
commit
d31620d0c6
@@ -19,3 +19,7 @@ if (WIN32)
|
||||
add_subdirectory(openvpn/ovpnagent/win)
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
add_subdirectory(openvpn/ovpnagent/mac)
|
||||
endif ()
|
||||
|
||||
|
||||
@@ -141,6 +141,12 @@ function (add_json_library target)
|
||||
target_link_libraries(${target} jsoncpp_lib)
|
||||
target_compile_definitions(${target} PRIVATE -DHAVE_JSONCPP)
|
||||
message("Adding jsoncpp to " ${target})
|
||||
elseif (APPLE)
|
||||
set(PLAT ${OPENVPN_PLAT})
|
||||
set(JSONCPP_DIR ${DEP_DIR}/jsoncpp/jsoncpp-${PLAT})
|
||||
target_include_directories(${target} PRIVATE ${JSONCPP_DIR}/include)
|
||||
target_compile_definitions(${target} PRIVATE -DHAVE_JSONCPP)
|
||||
target_link_libraries(${target} ${JSONCPP_DIR}/lib/libjsoncpp.a)
|
||||
else ()
|
||||
find_package(PkgConfig REQUIRED)
|
||||
if (MINGW)
|
||||
|
||||
78
deps/jsoncpp/build-jsoncpp
vendored
Executable file
78
deps/jsoncpp/build-jsoncpp
vendored
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
if [ -z "$O3" ]; then
|
||||
echo O3 var must point to ovpn3 tree
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$TARGET" ]; then
|
||||
echo TARGET var must be defined
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ -z "$DL" ] && DL=~/Downloads
|
||||
|
||||
# source vars
|
||||
. $O3/core/vars/vars-${TARGET}
|
||||
. $O3/core/deps/lib-versions
|
||||
|
||||
# source helper functions
|
||||
. $O3/core/deps/functions.sh
|
||||
|
||||
GPP=g++
|
||||
LD=ld
|
||||
AR=ar
|
||||
RANLIB=ranlib
|
||||
[ "$GPP_CMD" ] && GPP=$GPP_CMD
|
||||
[ "$LD_CMD" ] && LD=$LD_CMD
|
||||
[ "$AR_CMD" ] && AR=$AR_CMD
|
||||
[ "$RANLIB_CMD" ] && RANLIB=$RANLIB_CMD
|
||||
|
||||
case $PLATFORM in
|
||||
android*)
|
||||
echo PLATFORM android
|
||||
host="x86_64-apple-darwin"
|
||||
target=arm
|
||||
;;
|
||||
ios*)
|
||||
echo PLATFORM ios
|
||||
host="x86_64-apple-darwin"
|
||||
target=arm
|
||||
;;
|
||||
*)
|
||||
host=""
|
||||
target=""
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$target" ]; then
|
||||
targ_opt="--target=$target"
|
||||
fi
|
||||
|
||||
if [ "$host" ]; then
|
||||
host_opt="--host=$host"
|
||||
fi
|
||||
|
||||
FNAME=jsoncpp-${JSONCPP_VERSION}.tar.gz
|
||||
URL=https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/${JSONCPP_VERSION}.tar.gz
|
||||
CSUM=${JSONCPP_CSUM}
|
||||
|
||||
download
|
||||
|
||||
DIST=$(pwd)/jsoncpp/jsoncpp-$PLATFORM
|
||||
rm -rf $DIST
|
||||
mkdir -p $DIST/include
|
||||
mkdir $DIST/lib
|
||||
|
||||
tar xfz $DL/$FNAME
|
||||
|
||||
cd jsoncpp-$JSONCPP_VERSION
|
||||
python amalgamate.py
|
||||
cd dist
|
||||
CMD="$GPP -I. $PLATFORM_FLAGS $CXX_COMPILER_FLAGS $OTHER_COMPILER_FLAGS $LIB_OPT_LEVEL $LIB_FPIC -c jsoncpp.cpp"
|
||||
echo $CMD
|
||||
$CMD
|
||||
$AR rc $DIST/lib/libjsoncpp.a jsoncpp.o
|
||||
$RANLIB $DIST/lib/libjsoncpp.a
|
||||
cp -a json $DIST/include/
|
||||
exit 0
|
||||
2
openvpn/ovpnagent/mac/.gitignore
vendored
Normal file
2
openvpn/ovpnagent/mac/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
ovpnagent
|
||||
ovpnagent.dSYM
|
||||
6
openvpn/ovpnagent/mac/CMakeLists.txt
Normal file
6
openvpn/ovpnagent/mac/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
add_executable(agent_macos ovpnagent.cpp)
|
||||
add_core_dependencies(agent_macos)
|
||||
add_json_library(agent_macos)
|
||||
|
||||
# Avoid deprecated warning for using daemon
|
||||
target_compile_options(agent_macos PRIVATE -Wno-deprecated-declarations)
|
||||
13
openvpn/ovpnagent/mac/go
Executable file
13
openvpn/ovpnagent/mac/go
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
pushd $O3/core
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
. vars/vars-osx64
|
||||
. vars/setpath
|
||||
export GCC_EXTRA="$GCC_EXTRA -Wno-deprecated-declarations" # for daemon()
|
||||
else
|
||||
. vars/vars-linux
|
||||
. vars/setpath
|
||||
fi
|
||||
popd
|
||||
ASIO=1 MTLS=1 NOSSL=1 build ovpnagent
|
||||
1
openvpn/ovpnagent/mac/local.conf
Normal file
1
openvpn/ovpnagent/mac/local.conf
Normal file
@@ -0,0 +1 @@
|
||||
http-listen /tmp/agent.sock unix-stream
|
||||
767
openvpn/ovpnagent/mac/ovpnagent.cpp
Normal file
767
openvpn/ovpnagent/mac/ovpnagent.cpp
Normal file
@@ -0,0 +1,767 @@
|
||||
// OpenVPN
|
||||
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
|
||||
// All rights reserved
|
||||
|
||||
// OpenVPN agent for Mac
|
||||
|
||||
//#define OPENVPN_EXIT_IN 30
|
||||
|
||||
//#define OPENVPN_SSL_DEBUG 9 // MbedTLS debugging max level
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
// debug settings (production setting in parentheses)
|
||||
#define OPENVPN_LOG_SSL(x) OPENVPN_LOG(x)
|
||||
|
||||
// VERSION version can be passed on build command line
|
||||
#include <openvpn/common/stringize.hpp>
|
||||
#ifdef VERSION
|
||||
#define HTTP_SERVER_VERSION OPENVPN_STRINGIZE(VERSION)
|
||||
#else
|
||||
#define HTTP_SERVER_VERSION "0.1.1"
|
||||
#endif
|
||||
|
||||
#ifdef OVPNAGENT_NAME
|
||||
#define OVPNAGENT_NAME_STRING OPENVPN_STRINGIZE(OVPNAGENT_NAME)
|
||||
#else
|
||||
#define OVPNAGENT_NAME_STRING "ovpnagent"
|
||||
#endif
|
||||
|
||||
#include <openvpn/log/logbase.hpp>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/runcontext.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/getopt.hpp>
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
#include <openvpn/common/daemon.hpp>
|
||||
#include <openvpn/common/waitbarrier.hpp>
|
||||
#include <openvpn/common/usergroup.hpp>
|
||||
#include <openvpn/common/xmitfd.hpp>
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
#include <openvpn/init/initprocess.hpp>
|
||||
#include <openvpn/ssl/sslchoose.hpp>
|
||||
#include <openvpn/ws/httpserv.hpp>
|
||||
#include <openvpn/tun/mac/client/tunsetup.hpp>
|
||||
|
||||
void log_version()
|
||||
{
|
||||
std::cout << "OpenVPN Agent (Mac) " HTTP_SERVER_VERSION " [" SSL_LIB_NAME "]"
|
||||
#ifdef OPENVPN_DEBUG
|
||||
<< " built on " __DATE__ " " __TIME__
|
||||
#endif
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
using namespace openvpn;
|
||||
|
||||
class MySessionStats : public SessionStats
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<MySessionStats> Ptr;
|
||||
|
||||
virtual void error(const size_t err_type, const std::string* text=nullptr) override
|
||||
{
|
||||
OPENVPN_LOG(Error::name(err_type));
|
||||
}
|
||||
|
||||
std::string dump() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "OpenVPN Agent Stats" << std::endl;
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreadCommon
|
||||
{
|
||||
ThreadCommon(const char *unix_sock, const char *user, const char *group)
|
||||
: listen_list(build_listen_list(unix_sock)),
|
||||
user_group(user, group, true),
|
||||
stats(new MySessionStats),
|
||||
event_loop_bar(1)
|
||||
{
|
||||
}
|
||||
|
||||
static Listen::List build_listen_list(const char *unix_sock)
|
||||
{
|
||||
Listen::List ll;
|
||||
if (unix_sock)
|
||||
{
|
||||
Listen::Item li;
|
||||
li.directive = "http-listen";
|
||||
li.addr = unix_sock;
|
||||
li.proto = Protocol(Protocol::UnixStream);
|
||||
li.n_threads = 1;
|
||||
ll.push_back(std::move(li));
|
||||
}
|
||||
return ll;
|
||||
}
|
||||
|
||||
void show_unused_options() const
|
||||
{
|
||||
}
|
||||
|
||||
const Listen::List listen_list;
|
||||
const SetUserGroup user_group;
|
||||
MySessionStats::Ptr stats;
|
||||
PThreadBarrier event_loop_bar;
|
||||
};
|
||||
|
||||
class MyListener : public WS::Server::Listener
|
||||
{
|
||||
// handles ungraceful exit of client and closes tun
|
||||
class WatchdogThread : public RC<thread_safe_refcount>
|
||||
{
|
||||
private:
|
||||
typedef RCPtr<WatchdogThread> Ptr;
|
||||
|
||||
friend class MyListener;
|
||||
MyListener *parent;
|
||||
|
||||
public:
|
||||
WatchdogThread(MyListener *parent_arg, openvpn_io::io_context& io_context_arg)
|
||||
: parent(parent_arg), io_context(io_context_arg) { }
|
||||
|
||||
~WatchdogThread()
|
||||
{
|
||||
if (th.joinable())
|
||||
{
|
||||
OPENVPN_LOG("Reaping watchdog thread");
|
||||
th.join();
|
||||
}
|
||||
}
|
||||
|
||||
// starts a thread which blocks until:
|
||||
// - process with given pid exits
|
||||
// - there is a data in pipe
|
||||
void watch(pid_t pid)
|
||||
{
|
||||
if (client_pid != -1)
|
||||
{
|
||||
OPENVPN_LOG("Watchdog already set for pid " << client_pid << ", won't set for pid " << pid);
|
||||
return;
|
||||
}
|
||||
|
||||
OPENVPN_LOG("Setting up watchdog for pid " << pid << " exit notification");
|
||||
|
||||
// self-pipe trick to be able to interrupt wait when agent exits
|
||||
if (pipe(fds) == -1)
|
||||
{
|
||||
OPENVPN_LOG("pipe() failed: " << strerror(errno));
|
||||
return;
|
||||
}
|
||||
// make write nonblocking
|
||||
fcntl(fds[1], F_SETFL, O_NONBLOCK);
|
||||
|
||||
kq = kqueue();
|
||||
if (kq == -1)
|
||||
{
|
||||
OPENVPN_LOG("kqueue() failed: " << strerror(errno));
|
||||
close_pipe_fds();
|
||||
return;
|
||||
}
|
||||
|
||||
// add pid exit and self-pipe read_fd to kevent changelist
|
||||
struct kevent chlist[2];
|
||||
EV_SET(&chlist[0], pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, NULL);
|
||||
EV_SET(&chlist[1], fds[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
if (kevent(kq, chlist, 2, NULL, 0, NULL) == -1)
|
||||
{
|
||||
OPENVPN_LOG("kevent() failed: " << strerror(errno));
|
||||
close_pipe_fds();
|
||||
return;
|
||||
}
|
||||
|
||||
// reap thread if previous client didn't exit gracefullt
|
||||
if (th.joinable())
|
||||
{
|
||||
OPENVPN_LOG("Reaping watchdog thread");
|
||||
th.join();
|
||||
}
|
||||
|
||||
client_pid = pid;
|
||||
|
||||
th = std::thread([self=Ptr(this), pid]() {
|
||||
struct kevent evlist[2];
|
||||
int nev = kevent(self->kq, 0, 0, evlist, 2, NULL);
|
||||
if (nev == -1)
|
||||
{
|
||||
OPENVPN_LOG("kevent() failed: " << strerror(errno));
|
||||
self->close_pipe_fds();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nev; ++ i)
|
||||
{
|
||||
if (evlist[i].filter == EVFILT_PROC)
|
||||
{
|
||||
openvpn_io::post(self->io_context, [self, pid]() {
|
||||
OPENVPN_LOG("Process " << pid << " has exited, destroy tun");
|
||||
std::ostringstream os;
|
||||
self->parent->destroy_tun(os);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self->client_pid = -1;
|
||||
self->close_pipe_fds();
|
||||
});
|
||||
}
|
||||
|
||||
void unwatch()
|
||||
{
|
||||
OPENVPN_LOG("Stopping watchdog thread");
|
||||
write(fds[1], "x", 1);
|
||||
if (th.joinable())
|
||||
{
|
||||
OPENVPN_LOG("Reaping watchdog thread");
|
||||
th.join();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void close_pipe_fds()
|
||||
{
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
pid_t client_pid = -1;
|
||||
openvpn_io::io_context& io_context;
|
||||
int kq; // kqueue
|
||||
int fds[2]; // file descriptors for self-pipe trick
|
||||
std::thread th;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef RCPtr<MyListener> Ptr;
|
||||
|
||||
MyListener(openvpn_io::io_context& io_context,
|
||||
const WS::Server::Config::Ptr& config,
|
||||
const Listen::List& listen_list,
|
||||
const WS::Server::Listener::Client::Factory::Ptr& client_factory)
|
||||
: WS::Server::Listener(io_context, config, listen_list, client_factory)
|
||||
{
|
||||
watchdog.reset(new WatchdogThread(this, io_context));
|
||||
}
|
||||
|
||||
ScopedFD establish_tun(const TunBuilderCapture& tbc,
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os)
|
||||
{
|
||||
if (!tun)
|
||||
tun.reset(new TunMac::Setup);
|
||||
return ScopedFD(tun->establish(tbc, config, stop, os));
|
||||
}
|
||||
|
||||
void destroy_tun(std::ostream& os)
|
||||
{
|
||||
if (tun)
|
||||
{
|
||||
tun->destroy(os);
|
||||
tun.reset();
|
||||
}
|
||||
|
||||
remove_cmds_bypass_hosts.execute(os);
|
||||
remove_cmds_bypass_hosts.clear();
|
||||
}
|
||||
|
||||
void set_watchdog(pid_t pid)
|
||||
{
|
||||
watchdog->watch(pid);
|
||||
}
|
||||
|
||||
void unset_watchdog()
|
||||
{
|
||||
watchdog->unwatch();
|
||||
}
|
||||
|
||||
void add_bypass_route(const std::string& host, bool ipv6)
|
||||
{
|
||||
if (host != bypass_host)
|
||||
{
|
||||
bypass_host = host;
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
remove_cmds_bypass_hosts.execute(os);
|
||||
remove_cmds_bypass_hosts.clear();
|
||||
|
||||
ActionList add_cmds;
|
||||
TunMac::Setup::add_bypass_route(host, ipv6, add_cmds, remove_cmds_bypass_hosts);
|
||||
add_cmds.execute(os);
|
||||
|
||||
OPENVPN_LOG(os.str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool allow_client(AsioPolySock::Base& sock) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string bypass_host;
|
||||
ActionList remove_cmds_bypass_hosts;
|
||||
|
||||
TunMac::Setup::Ptr tun;
|
||||
WatchdogThread::Ptr watchdog;
|
||||
};
|
||||
|
||||
class MyClientInstance : public WS::Server::Listener::Client
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<MyClientInstance> Ptr;
|
||||
|
||||
MyClientInstance(WS::Server::Listener::Client::Initializer& ci)
|
||||
: WS::Server::Listener::Client(ci)
|
||||
{
|
||||
//OPENVPN_LOG("INSTANCE START");
|
||||
}
|
||||
|
||||
virtual ~MyClientInstance()
|
||||
{
|
||||
//OPENVPN_LOG("INSTANCE DESTRUCT");
|
||||
}
|
||||
|
||||
private:
|
||||
void generate_reply(const Json::Value& jout)
|
||||
{
|
||||
out = buf_from_string(jout.toStyledString());
|
||||
|
||||
WS::Server::ContentInfo ci;
|
||||
ci.http_status = HTTP::Status::OK;
|
||||
ci.type = "application/json";
|
||||
ci.length = out->size();
|
||||
ci.keepalive = keepalive_request();
|
||||
generate_reply_headers(ci);
|
||||
}
|
||||
|
||||
virtual void http_request_received() override
|
||||
{
|
||||
// alloc output buffer
|
||||
std::ostringstream os;
|
||||
|
||||
try {
|
||||
const HTTP::Request& req = request();
|
||||
OPENVPN_LOG("HTTP request received from " << sock->remote_endpoint_str() << '\n' << req.to_string());
|
||||
|
||||
// get content-type
|
||||
const std::string content_type = req.headers.get_value_trim("content-type");
|
||||
|
||||
if (req.method == "POST")
|
||||
{
|
||||
// verify correct content-type
|
||||
if (string::strcasecmp(content_type, "application/json"))
|
||||
throw Exception("bad content-type");
|
||||
|
||||
// parse the json dict
|
||||
const Json::Value root = json::parse(in.to_string(), "JSON request");
|
||||
if (!root.isObject())
|
||||
throw Exception("json parse error: top level json object is not a dictionary");
|
||||
|
||||
if (req.uri == "/tun-setup")
|
||||
{
|
||||
send_fd.reset();
|
||||
|
||||
// get PID
|
||||
pid_t pid = json::get_int_optional(root, "pid", -1);
|
||||
if (pid != -1)
|
||||
parent()->set_watchdog(pid);
|
||||
|
||||
// parse JSON data into a TunBuilderCapture object
|
||||
TunBuilderCapture::Ptr tbc = TunBuilderCapture::from_json(json::get_dict(root, "tun", false));
|
||||
tbc->validate();
|
||||
|
||||
// get config
|
||||
TunMac::Setup::Config config;
|
||||
config.from_json(json::get_dict(root, "config", false), "config");
|
||||
|
||||
// establish the tun setup object
|
||||
send_fd = parent()->establish_tun(*tbc, &config, nullptr, os);
|
||||
|
||||
// build JSON return dictionary
|
||||
Json::Value jout(Json::objectValue);
|
||||
jout["log_txt"] = Json::Value(string::remove_blanks(os.str()));
|
||||
jout["config"] = config.to_json();
|
||||
generate_reply(jout);
|
||||
}
|
||||
else if (req.uri == "/add-bypass-route")
|
||||
{
|
||||
pid_t pid = json::get_int_optional(root, "pid", -1);
|
||||
if (pid != -1)
|
||||
parent()->set_watchdog(pid);
|
||||
bool ipv6 = json::get_bool(root, "ipv6");
|
||||
std::string host = json::get_string(root, "host");
|
||||
|
||||
parent()->add_bypass_route(host, ipv6);
|
||||
|
||||
Json::Value jout(Json::objectValue);
|
||||
generate_reply(jout);
|
||||
}
|
||||
}
|
||||
else if (req.method == "GET" && req.uri == "/tun-destroy")
|
||||
{
|
||||
// destroy tun object
|
||||
parent()->destroy_tun(os);
|
||||
|
||||
// build JSON return dictionary
|
||||
Json::Value jout(Json::objectValue);
|
||||
jout["log_txt"] = Json::Value(string::remove_blanks(os.str()));
|
||||
generate_reply(jout);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = buf_from_string("page not found\n");
|
||||
WS::Server::ContentInfo ci;
|
||||
ci.http_status = HTTP::Status::NotFound;
|
||||
ci.type = "text/plain";
|
||||
ci.length = out->size();
|
||||
generate_reply_headers(ci);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
out = buf_from_string(string::remove_blanks(os.str() + e.what() + '\n'));
|
||||
WS::Server::ContentInfo ci;
|
||||
ci.http_status = HTTP::Status::BadRequest;
|
||||
ci.type = "text/plain";
|
||||
ci.length = out->size();
|
||||
generate_reply_headers(ci);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void http_content_in(BufferAllocated& buf) override
|
||||
{
|
||||
if (buf.defined())
|
||||
in.emplace_back(new BufferAllocated(std::move(buf)));
|
||||
}
|
||||
|
||||
virtual BufferPtr http_content_out() override
|
||||
{
|
||||
BufferPtr ret;
|
||||
ret.swap(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Normally true is returned, however return false if we
|
||||
// are planning to send the tun file descriptor to the client.
|
||||
virtual bool http_out_eof() override
|
||||
{
|
||||
//OPENVPN_LOG("HTTP output EOF send_fd=" << send_fd());
|
||||
return !send_fd.defined();
|
||||
}
|
||||
|
||||
// After HTTP reply has been transmitted, wait for client to
|
||||
// send a 't' message. On receipt, reply with a 'T' message
|
||||
// that bundles the tun file descriptor.
|
||||
virtual void http_pipeline_peek(BufferAllocated& buf) override
|
||||
{
|
||||
//OPENVPN_LOG("HTTP PIPELINE PEEK send_fd=" << send_fd() << " CONTENT=" << buf_to_string(buf));
|
||||
if (send_fd.defined())
|
||||
{
|
||||
if (buf.size() == 1 && buf.front() == 't')
|
||||
{
|
||||
const int fd = unix_fd();
|
||||
if (fd < 0)
|
||||
OPENVPN_THROW_EXCEPTION("http_pipeline_peek: not a unix socket");
|
||||
XmitFD::xmit_fd(fd, send_fd(), "T", 5000);
|
||||
external_stop("FD transmitted");
|
||||
}
|
||||
else
|
||||
OPENVPN_THROW_EXCEPTION("bad FD request message");
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool http_stop(const int status, const std::string& description) override
|
||||
{
|
||||
OPENVPN_LOG("INSTANCE STOP : " << WS::Server::Status::error_str(status) << " : " << description);
|
||||
|
||||
// if the shutdown happened due to an unexpected error, the TUN status has
|
||||
// to be cleaned up to avoid configuration inconsistency.
|
||||
//
|
||||
// A problem we have witnessed was that the DNS settings were not being
|
||||
// reverted when the HTTP connection with the core was interrupted in the
|
||||
// middle of the establish() call.
|
||||
if (status != WS::Server::Status::E_SUCCESS &&
|
||||
status != WS::Server::Status::E_EXTERNAL_STOP)
|
||||
{
|
||||
std::ostringstream os;
|
||||
parent()->destroy_tun(os);
|
||||
}
|
||||
|
||||
// returning true here triggers socket shutdown, which triggers "Socket is not connected" error
|
||||
return false;
|
||||
}
|
||||
|
||||
MyListener* parent()
|
||||
{
|
||||
return static_cast<MyListener*>(get_parent());
|
||||
}
|
||||
|
||||
ScopedFD send_fd;
|
||||
BufferList in;
|
||||
BufferPtr out;
|
||||
};
|
||||
|
||||
class MyClientFactory : public WS::Server::Listener::Client::Factory
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<MyClientFactory> Ptr;
|
||||
|
||||
virtual WS::Server::Listener::Client::Ptr new_client(WS::Server::Listener::Client::Initializer& ci) override
|
||||
{
|
||||
return new MyClientInstance(ci);
|
||||
}
|
||||
};
|
||||
|
||||
class ServerThread : public ServerThreadBase
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<ServerThread> Ptr;
|
||||
|
||||
ServerThread(openvpn_io::io_context& io_context_arg,
|
||||
ThreadCommon& tc)
|
||||
: io_context(io_context_arg),
|
||||
halt(false)
|
||||
{
|
||||
Frame::Ptr frame = frame_init_simple(2048);
|
||||
|
||||
WS::Server::Config::Ptr config = new WS::Server::Config();
|
||||
config->http_server_id = OVPNAGENT_NAME_STRING "/" HTTP_SERVER_VERSION;
|
||||
config->frame = frame;
|
||||
config->stats = tc.stats;
|
||||
config->unix_mode = 0777;
|
||||
|
||||
MyClientFactory::Ptr factory = new MyClientFactory();
|
||||
listener.reset(new MyListener(io_context_arg, config, tc.listen_list, factory));
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
listener->start();
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
halt = true;
|
||||
listener->stop();
|
||||
listener->unset_watchdog();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void thread_safe_stop() override
|
||||
{
|
||||
if (!halt)
|
||||
{
|
||||
openvpn_io::post(io_context, [self=Ptr(this)]()
|
||||
{
|
||||
self->stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
openvpn_io::io_context& io_context;
|
||||
MyListener::Ptr listener;
|
||||
bool halt;
|
||||
};
|
||||
|
||||
typedef RunContext<ServerThreadBase, MySessionStats> MyRunContext;
|
||||
|
||||
void work(openvpn_io::io_context& io_context,
|
||||
ThreadCommon& tc,
|
||||
MyRunContext& runctx,
|
||||
const unsigned int unit)
|
||||
{
|
||||
ServerThread::Ptr serv;
|
||||
|
||||
try {
|
||||
serv.reset(new ServerThread(io_context, tc));
|
||||
runctx.set_server(unit, serv.get());
|
||||
|
||||
serv->start();
|
||||
|
||||
// barrier prior to event-loop entry
|
||||
event_loop_wait_barrier(tc);
|
||||
|
||||
// privilege has now been downgraded
|
||||
|
||||
// run i/o reactor
|
||||
io_context.run();
|
||||
runctx.clear_server(unit);
|
||||
serv->stop();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tc.event_loop_bar.error();
|
||||
if (serv)
|
||||
{
|
||||
runctx.clear_server(unit);
|
||||
serv->stop(); // on exception, stop server
|
||||
}
|
||||
io_context.poll(); // execute completion handlers
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void worker_thread(ThreadCommon& tc,
|
||||
MyRunContext& runctx,
|
||||
const unsigned int unit)
|
||||
{
|
||||
SignalBlockerDefault signal_blocker;
|
||||
openvpn_io::io_context io_context(1); // concurrency hint=1
|
||||
Log::Context log_context(runctx.log_wrapper());
|
||||
MyRunContext::ThreadContext thread_ctx(runctx);
|
||||
|
||||
try {
|
||||
work(io_context, tc, runctx, unit);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("Worker thread exception: " << e.what());
|
||||
}
|
||||
}
|
||||
|
||||
int ovpnagent(const char *sock_fn,
|
||||
const char *log_fn,
|
||||
const bool log_append,
|
||||
const char *pid_fn,
|
||||
const char *user,
|
||||
const char *group)
|
||||
{
|
||||
if (log_fn)
|
||||
daemonize(log_fn, nullptr, log_append, 0);
|
||||
|
||||
if (pid_fn)
|
||||
write_pid(pid_fn);
|
||||
|
||||
log_version();
|
||||
|
||||
MyRunContext::Ptr runctx(new MyRunContext());
|
||||
ThreadCommon tc(sock_fn, user, group);
|
||||
|
||||
// Give runctx visibility into global stats
|
||||
// for SIGUSR2 dump.
|
||||
runctx->set_stats_obj(tc.stats);
|
||||
|
||||
// Main worker thread
|
||||
{
|
||||
const unsigned int thread_num = 0;
|
||||
std::thread* thread = new std::thread([&tc, &runctx]() {
|
||||
worker_thread(tc, *runctx, thread_num);
|
||||
});
|
||||
runctx->set_thread(thread_num, thread);
|
||||
}
|
||||
|
||||
// wait for worker to exit
|
||||
runctx->run();
|
||||
runctx->join();
|
||||
|
||||
// dump final stats
|
||||
OPENVPN_LOG_NTNL(tc.stats->dump());
|
||||
|
||||
// remove pidfile
|
||||
if (pid_fn)
|
||||
::unlink(pid_fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(usage);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
static const struct option longopts[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "append", no_argument, nullptr, 'a' },
|
||||
{ "daemon", required_argument, nullptr, 'd' },
|
||||
{ "pidfile", required_argument, nullptr, 'p' },
|
||||
{ "user", required_argument, nullptr, 'u' },
|
||||
{ "group", required_argument, nullptr, 'g' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
int ret = 0;
|
||||
|
||||
bool append = false;
|
||||
const char *logfile = nullptr;
|
||||
const char *pidfile = nullptr;
|
||||
const char *user = nullptr;
|
||||
const char *group = nullptr;
|
||||
|
||||
// process-wide initialization
|
||||
InitProcess::Init init;
|
||||
|
||||
// set global MbedTLS debug level
|
||||
#if defined(USE_MBEDTLS) && defined(OPENVPN_SSL_DEBUG)
|
||||
debug_set_threshold(OPENVPN_SSL_DEBUG);
|
||||
#endif
|
||||
|
||||
try {
|
||||
if (argc < 1)
|
||||
throw usage();
|
||||
|
||||
int ch;
|
||||
while ((ch = getopt_long(argc, argv, "had:p:u:g:", longopts, nullptr)) != -1)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'a':
|
||||
append = true;
|
||||
break;
|
||||
case 'd':
|
||||
logfile = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
pidfile = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 'g':
|
||||
group = optarg;
|
||||
break;
|
||||
default:
|
||||
throw usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
ret = ovpnagent("/var/run/" OVPNAGENT_NAME_STRING ".sock", logfile, append, pidfile, user, group);
|
||||
}
|
||||
catch (const usage&)
|
||||
{
|
||||
log_version();
|
||||
std::cout << "usage: ovpnagent [options]" << std::endl;
|
||||
std::cout << " --daemon <file>, -d : daemonize, log to file" << std::endl;
|
||||
std::cout << " --append, -a : append to log file" << std::endl;
|
||||
std::cout << " --pidfile <file>, -p : write pid to file" << std::endl;
|
||||
std::cout << " --user <user>, -u : set UID to user" << std::endl;
|
||||
std::cout << " --group <group>, -g : set group" << std::endl;
|
||||
ret = 2;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Main thread exception: " << e.what() << std::endl;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
9
scripts/build-extras/jsoncpp.sh
Normal file
9
scripts/build-extras/jsoncpp.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
if [ "$1" = "args" ]; then
|
||||
echo " JSON=1 -- build with JsonCpp library"
|
||||
elif [ "$1" = "deps" ]; then
|
||||
# JsonCpp
|
||||
if [ "$JSON" = "1" ]; then
|
||||
CPPFLAGS="$CPPFLAGS $(pkg-config --cflags jsoncpp) -DHAVE_JSONCPP"
|
||||
EXTRA_SRC_OBJ="$EXTRA_SRC_OBJ $(pkg-config --libs jsoncpp)"
|
||||
fi
|
||||
fi
|
||||
@@ -5,7 +5,7 @@ if [ -z "$O3" ]; then
|
||||
echo O3 var must point to ovpn3 tree ; exit 1
|
||||
fi
|
||||
cd $DEP_DIR
|
||||
rm -rf asio* boost* lz4* lzo* minicrypto openssl* polarssl* mbedtls* snappy*
|
||||
rm -rf asio* boost* lz4* lzo* minicrypto openssl* polarssl* mbedtls* snappy* jsoncpp*
|
||||
|
||||
echo "******* ASIO"
|
||||
$O3/core/deps/asio/build-asio
|
||||
@@ -33,6 +33,9 @@ $O3/core/scripts/mac/build-lz4
|
||||
echo "******* xxHash"
|
||||
$O3/core/deps/xxHash/build-xxHash
|
||||
|
||||
echo "******* JSONCPP"
|
||||
$O3/core/scripts/mac/build-jsoncpp
|
||||
|
||||
|
||||
#echo "******* MINICRYPTO"
|
||||
#$O3/core/scripts/mac/build-minicrypto
|
||||
|
||||
28
scripts/mac/build-jsoncpp
Executable file
28
scripts/mac/build-jsoncpp
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
if [ -z "$O3" ]; then
|
||||
echo O3 var must point to ovpn3 tree
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$DEP_DIR" ]; then
|
||||
echo DEP_DIR var must point to ovpn3 dependency tree
|
||||
exit 1
|
||||
fi
|
||||
cd $DEP_DIR
|
||||
|
||||
rm -rf jsoncpp
|
||||
mkdir jsoncpp
|
||||
|
||||
if [ "$OSX_ONLY" != "1" ]; then
|
||||
for target in ios ios-dbg iossim iossim-dbg ; do
|
||||
echo '***************' TARGET $target
|
||||
TARGET=$target $O3/core/deps/jsoncpp/build-jsoncpp
|
||||
done
|
||||
fi
|
||||
|
||||
for target in osx osx-dbg ; do
|
||||
echo '***************' TARGET $target
|
||||
TARGET=$target $O3/core/deps/jsoncpp/build-jsoncpp
|
||||
done
|
||||
exit 0
|
||||
@@ -39,7 +39,6 @@ if (${CLI_OVPNDCO})
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# for macos, agent-enabled client requires common and is built by scripts there
|
||||
add_executable(ovpncliagent cli.cpp)
|
||||
add_core_dependencies(ovpncliagent)
|
||||
add_json_library(ovpncliagent)
|
||||
@@ -56,3 +55,10 @@ if (WIN32)
|
||||
target_include_directories(ovpncli PRIVATE ${OVPN_DCO_WIN_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
add_executable(ovpncliagent cli.cpp)
|
||||
add_core_dependencies(ovpncliagent)
|
||||
add_json_library(ovpncliagent)
|
||||
target_compile_definitions(ovpncliagent PRIVATE -DOPENVPN_COMMAND_AGENT)
|
||||
endif ()
|
||||
|
||||
Reference in New Issue
Block a user