Initial import

This commit is contained in:
Peter Sykora 2021-08-20 17:58:25 +02:00
commit d2051dcb28
12 changed files with 1382 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/out/

17
CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required (VERSION 3.8)
if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")
set (CMAKE_SYSTEM_VERSION 8.1 CACHE TYPE INTERNAL FORCE) #Force 8.1 SDK, to keep it compatible with win7
endif()
project ("CMakeProject1")
IF(WIN32)
ADD_DEFINITIONS(/bigobj)
ENDIF(WIN32)
# Include sub-projects.
add_subdirectory ("CMakeProject1")

857
CMakeProject1/CCServer.cpp Normal file
View File

@ -0,0 +1,857 @@
#include "CCServer.h"
#include "CryptoUtils.h"
//------------------------------------------------------------------------------
//
// Based on Beast example: Advanced server
//
//------------------------------------------------------------------------------
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/strand.hpp>
#include <boost/crc.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/filesystem.hpp>
#include <boost/signals2.hpp>
#include <boost/tokenizer.hpp>
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <fstream>
#include <shared_mutex>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
namespace fs = boost::filesystem; // from <boost/filesystem.hpp>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
static constexpr auto APIS_UINT8_MIN = std::numeric_limits<uint8_t>::min();
static constexpr auto APIS_UINT8_MAX = std::numeric_limits<uint8_t>::max();
#define MAXHESLO 225
#define MAX_DEKODHESLO (MAXHESLO-3)
#define KodcharToChar(znak2,poloha,posun) static_cast<uint8_t>(-(password[poloha])+(~(znak2))-(posun))
namespace
{
static const std::string cryptFileHeader("##CAPISF## Copyright (c) 2003 APIS spol. s r.o. SLOVAKIA~");
//static const std::string passReal("flckdscwjregfvfgbujvbnqajmadmgfjmhfmghplgghesrrhsufservvysfidgfysdnmsybsaftoprecofoplyrmnfsvcapridssedalenttrmfusposvresalbostreqnnapswolnfrdweneplmnzcavsfgusqootrpqaressnvclpacfdrgesklojphsdqtyczvxcbfhtryntinokusgkhjgchtx");
static const uint8_t passHidden2[] = { 0x89, 0xdb, 0x2c, 0x79, 0xd6, 0xa5, 0x7a, 0x7b, 0x6d, 0xae, 0x67, 0xee, 0xb2, 0x9a, 0x2c, 0xbe, 0xb7, 0xac, 0x6a, 0x56, 0xe8, 0xb2, 0xda, 0xde, 0xaa, 0x79, 0xda, 0xa6, 0xcc, 0x28, 0x96, 0x77, 0xeb, 0x77, 0x07, 0xa7, 0x7a, 0x99, 0x66, 0x9f, 0x37, 0x1a, 0xbe, 0xc7, 0xe0, 0xba, 0xca, 0xa8, 0xa2, 0xda, 0xe9, 0xa9, 0xaa, 0xde, 0xb2, 0xc9, 0xef, 0x72, 0x5a, 0x5a, 0x71, 0xf7, 0x6b, 0x81, 0xeb, 0x24, 0x96, 0x88, 0xe9, 0x86, 0xc7, 0x6a, 0xb7, 0x27, 0x33, 0xbf, 0x17, 0x1b, 0x7e, 0x1b, 0x6b, 0xca, 0x7b, 0x62, 0x9e, 0x89, 0x2e, 0xb2, 0x09, 0x21, 0x8e, 0x07, 0x21, 0xb7, 0x10, 0x34 };
class apis_read_filter
{
public:
typedef char char_type;
explicit apis_read_filter(size_t initialPosition)
: m_position(initialPosition)
{ }
struct category
: boost::iostreams::input
, boost::iostreams::filter_tag
, boost::iostreams::multichar_tag
, boost::iostreams::optimally_buffered_tag
{};
std::streamsize optimal_buffer_size() const { return 0; }
template<typename Source>
std::streamsize read(Source& src, char_type* s, std::streamsize n)
{
std::streamsize result = boost::iostreams::read(src, s, n);
if (result == -1)
{
return -1;
}
int dwPacketPos = ((m_position / MAX_DEKODHESLO) % 29) + 2;
int iPoloha = m_position % MAX_DEKODHESLO;
m_position += static_cast<size_t>(result);
for (int i = 0; i < result; i++)
{
if (iPoloha == MAX_DEKODHESLO)
{
iPoloha = 0;
dwPacketPos++;
}
if (dwPacketPos >= 31)
{
dwPacketPos = 2;
}
static const uint8_t passHidden[] = { 0x7e, 0x57, 0x24, 0x76, 0xc7, 0x30, 0x8e, 0xb7, 0xa0, 0x7e, 0xf7, 0xe0, 0x6e, 0xe8, 0xef, 0x6e, 0x7a, 0x9a, 0x8e, 0x66, 0x9d, 0x9a, 0x07, 0xe3, 0x9a, 0x17, 0xe6, 0x82, 0x1a, 0x65, 0x82, 0x08, 0x5e, 0xb2, 0xba, 0xe1, 0xb2, 0xe7, 0xec, 0x7a, 0xbb, 0xef, 0xca, 0xc7, 0xe2, 0x76, 0x07, 0xf2, 0xb1, 0xd9, 0xe6, 0xb3, 0x26, 0xec, 0x69, 0xfb, 0x68, 0xa6, 0xb7, 0x9c, 0xa1, 0xfa, 0x29, 0x97, 0x2a, 0xe6, 0x9d, 0xfb, 0x2f, 0x71, 0xaa, 0x6b };
if (iPoloha < sizeof(passHidden) * 4 / 3)
{
static const auto password = base64Encode(passHidden, sizeof(passHidden));
s[i] = KodcharToChar(s[i], iPoloha, iPoloha + dwPacketPos);
}
else
{
static const auto password = base64Encode(passHidden2, sizeof(passHidden2));
s[i] = KodcharToChar(s[i], iPoloha - sizeof(passHidden) * 4 / 3, iPoloha + dwPacketPos);
}
++iPoloha;
}
return result;
}
private:
size_t m_position;
};
void decryptBuffer(std::vector<uint8_t>& buffer)
{
bool encrypted = false;
if (buffer.size() >= cryptFileHeader.size())
{
encrypted = std::equal(cryptFileHeader.begin(), cryptFileHeader.end(), buffer.begin());
}
if (!encrypted)
{
return;
}
boost::iostreams::array_source src{ (const char*)buffer.data() + cryptFileHeader.size(), buffer.size() - cryptFileHeader.size()};
boost::iostreams::filtering_istream is;
is.push(apis_read_filter({ cryptFileHeader.size() }));
is.push(src);
std::vector<uint8_t> decrypted(buffer.size() - cryptFileHeader.size());
is.read((char*)decrypted.data(), decrypted.size());
if (decrypted.size() > sizeof(uint32_t))
{
auto headerLen = reinterpret_cast<uint32_t*>(decrypted.data())[0];
if (decrypted.size() > headerLen)
{
decrypted.erase(decrypted.begin(), decrypted.begin() + headerLen);
}
}
buffer = decrypted;
}
class etag_cache
{
public:
void insert(boost::beast::string_view path, boost::beast::string_view etag)
{
std::unique_lock<std::shared_mutex> lock(mutex_);
map_.emplace(path, etag);
}
bool check_path(boost::beast::string_view path, boost::beast::string_view etag) const
{
std::shared_lock<std::shared_mutex> lock(mutex_);
auto it = map_.find(static_cast<std::string>(path));
if (it != map_.end() && it->second == etag)
{
return true;
}
return false;
}
void clear()
{
std::unique_lock<std::shared_mutex> lock(mutex_);
map_.clear();
}
private:
mutable std::shared_mutex mutex_;
std::unordered_map<std::string, std::string> map_;
};
// Return a reasonable mime type based on the extension of a file.
beast::string_view
mime_type(beast::string_view path)
{
using beast::iequals;
auto const ext = [&path]
{
auto const pos = path.rfind(".");
if (pos == beast::string_view::npos)
return beast::string_view{};
return path.substr(pos);
}();
if (iequals(ext, ".htm")) return "text/html";
if (iequals(ext, ".html")) return "text/html";
if (iequals(ext, ".php")) return "text/html";
if (iequals(ext, ".css")) return "text/css";
if (iequals(ext, ".txt")) return "text/plain";
if (iequals(ext, ".js")) return "application/javascript";
if (iequals(ext, ".json")) return "application/json";
if (iequals(ext, ".xml")) return "application/xml";
if (iequals(ext, ".swf")) return "application/x-shockwave-flash";
if (iequals(ext, ".flv")) return "video/x-flv";
if (iequals(ext, ".png")) return "image/png";
if (iequals(ext, ".jpe")) return "image/jpeg";
if (iequals(ext, ".jpeg")) return "image/jpeg";
if (iequals(ext, ".jpg")) return "image/jpeg";
if (iequals(ext, ".gif")) return "image/gif";
if (iequals(ext, ".bmp")) return "image/bmp";
if (iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
if (iequals(ext, ".tiff")) return "image/tiff";
if (iequals(ext, ".tif")) return "image/tiff";
if (iequals(ext, ".svg")) return "image/svg+xml";
if (iequals(ext, ".svgz")) return "image/svg+xml";
return "application/text";
}
// Append an HTTP rel-path to a local filesystem path.
// The returned path is normalized for the platform.
std::string
path_cat(
beast::string_view base,
beast::string_view path)
{
if (base.empty())
return std::string(path);
std::string result(base);
#ifdef BOOST_MSVC
char constexpr path_separator = '\\';
if (result.back() == path_separator)
result.resize(result.size() - 1);
result.append(path.data(), path.size());
for (auto& c : result)
if (c == '/')
c = path_separator;
#else
char constexpr path_separator = '/';
if (result.back() == path_separator)
result.resize(result.size() - 1);
result.append(path.data(), path.size());
#endif
return result;
}
// This function produces an HTTP response for the given
// request. The type of the response object depends on the
// contents of the request, so the interface requires the
// caller to pass a generic lambda for receiving the response.
template<
class Body, class Allocator,
class Send>
void
handle_request(
beast::string_view doc_root,
etag_cache& cache,
http::request<Body, http::basic_fields<Allocator>>&& req,
Send&& send)
{
// Returns a bad request response
auto const bad_request =
[&req](beast::string_view why)
{
http::response<http::string_body> res{ http::status::bad_request, req.version() };
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = std::string(why);
res.prepare_payload();
return res;
};
// Returns a not found response
auto const not_found =
[&req](beast::string_view target)
{
http::response<http::string_body> res{ http::status::not_found, req.version() };
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = "The resource '" + std::string(target) + "' was not found.";
res.prepare_payload();
return res;
};
// Returns a server error response
auto const server_error =
[&req](beast::string_view what)
{
http::response<http::string_body> res{ http::status::internal_server_error, req.version() };
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = "An error occurred: '" + std::string(what) + "'";
res.prepare_payload();
return res;
};
// Returns a not modified response
auto const not_modified =
[&req](boost::beast::string_view etag)
{
http::response<http::empty_body> res{ http::status::not_modified, req.version() };
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::etag, etag);
res.keep_alive(req.keep_alive());
res.prepare_payload();
return res;
};
// Make sure we can handle the method
if (req.method() != http::verb::get &&
req.method() != http::verb::head)
return send(bad_request("Unknown HTTP-method"));
// Request path must be absolute and not contain "..".
if (req.target().empty() ||
req.target()[0] != '/' ||
req.target().find("..") != beast::string_view::npos)
return send(bad_request("Illegal request-target"));
auto it = req.find(http::field::if_none_match);
if (it != req.end())
{
auto etags = std::string(it->value());
typedef boost::tokenizer<boost::char_separator<char>> Tokenizer;
boost::char_separator<char> sep(", ");
Tokenizer tok{ etags, sep };
for (Tokenizer::iterator tokIt = tok.begin(); tokIt != tok.end(); ++tokIt)
{
std::string etag(*tokIt);
boost::string_view checksum(etag);
if (checksum.front() == '"')
{
checksum.remove_prefix(1);
checksum.remove_suffix(1);
}
if (cache.check_path(req.target(), checksum))
{
return send(not_modified(etag));
}
}
}
// Build the path to the requested file
beast::string_view targetPath = req.target();
auto pos = targetPath.find_first_of('?');
if (pos != std::string::npos)
{
targetPath = targetPath.substr(0, pos);
}
std::string path = path_cat(doc_root, targetPath);
if (targetPath.back() == '/')
path.append("index.htm");
// Attempt to open the file
if (!boost::filesystem::is_regular_file(path))
{
return send(not_found(req.target()));
}
std::ifstream body(path, std::ifstream::binary);
body.seekg(0, std::ios::end);
auto size = static_cast<size_t>(body.tellg());
std::vector<uint8_t> buffer(size, 0);
body.seekg(0);
body.read((char*)buffer.data(), size);
body.close();
decryptBuffer(buffer);
size = buffer.size();
boost::crc_32_type result;
result.process_bytes(buffer.data(), buffer.size());
std::ostringstream checksum;
checksum << std::hex << result.checksum();
auto etag = std::string("\"") + checksum.str() + "\"";
cache.insert(req.target(), checksum.str());
// Respond to HEAD request
if (req.method() == http::verb::head)
{
http::response<http::empty_body> res{ http::status::ok, req.version() };
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, mime_type(path));
res.content_length(size);
res.keep_alive(req.keep_alive());
return send(std::move(res));
}
// Respond to GET request
// TODO: replace this by decryption
http::response<http::buffer_body> res{ http::status::ok, req.version() };
res.set(http::field::server, "Based on " BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, mime_type(path));
res.set(http::field::etag, etag);
res.body().data = buffer.data();
res.body().size = buffer.size();
res.body().more = false;
res.content_length(size);
res.keep_alive(req.keep_alive());
return send(std::move(res));
}
//------------------------------------------------------------------------------
// Report a failure
void
fail(beast::error_code ec, char const* what)
{
std::cerr << what << ": " << ec.message() << "\n";
}
// Handles an HTTP server connection
class http_session : public std::enable_shared_from_this<http_session>
{
// This queue is used for HTTP pipelining.
class queue
{
enum
{
// Maximum number of responses we will queue
limit = 8
};
// The type-erased, saved work item
struct work
{
virtual ~work() = default;
virtual void operator()() = 0;
};
http_session& self_;
std::vector<std::unique_ptr<work>> items_;
public:
explicit
queue(http_session& self)
: self_(self)
{
static_assert(limit > 0, "queue limit must be positive");
items_.reserve(limit);
}
// Returns `true` if we have reached the queue limit
bool
is_full() const
{
return items_.size() >= limit;
}
// Called when a message finishes sending
// Returns `true` if the caller should initiate a read
bool
on_write()
{
BOOST_ASSERT(!items_.empty());
auto const was_full = is_full();
items_.erase(items_.begin());
if (!items_.empty())
(*items_.front())();
return was_full;
}
// Called by the HTTP handler to send a response.
template<bool isRequest, class Body, class Fields>
void
operator()(http::message<isRequest, Body, Fields>&& msg)
{
// This holds a work item
struct work_impl : work
{
http_session& self_;
http::message<isRequest, Body, Fields> msg_;
work_impl(
http_session& self,
http::message<isRequest, Body, Fields>&& msg)
: self_(self)
, msg_(std::move(msg))
{
}
void
operator()()
{
http::async_write(
self_.stream_,
msg_,
beast::bind_front_handler(
&http_session::on_write,
self_.shared_from_this(),
msg_.need_eof()));
}
};
// Allocate and store the work
items_.push_back(
boost::make_unique<work_impl>(self_, std::move(msg)));
// If there was no previous work, start this one
if (items_.size() == 1)
(*items_.front())();
}
};
beast::tcp_stream stream_;
beast::flat_buffer buffer_;
std::shared_ptr<std::string const> doc_root_;
etag_cache& etag_cache_;
queue queue_;
// The parser is stored in an optional container so we can
// construct it from scratch it at the beginning of each new message.
boost::optional<http::request_parser<http::string_body>> parser_;
public:
// Take ownership of the socket
http_session(
tcp::socket&& socket,
std::shared_ptr<std::string const> const& doc_root,
etag_cache& etag_cache)
: stream_(std::move(socket))
, doc_root_(doc_root)
, etag_cache_(etag_cache)
, queue_(*this)
{
}
// Start the session
void
run()
{
// We need to be executing within a strand to perform async operations
// on the I/O objects in this session. Although not strictly necessary
// for single-threaded contexts, this example code is written to be
// thread-safe by default.
net::dispatch(
stream_.get_executor(),
beast::bind_front_handler(
&http_session::do_read,
this->shared_from_this()));
}
private:
void
do_read()
{
// Construct a new parser for each message
parser_.emplace();
// Apply a reasonable limit to the allowed size
// of the body in bytes to prevent abuse.
parser_->body_limit(10000);
// Set the timeout.
stream_.expires_after(std::chrono::seconds(30));
// Read a request using the parser-oriented interface
http::async_read(
stream_,
buffer_,
*parser_,
beast::bind_front_handler(
&http_session::on_read,
shared_from_this()));
}
void
on_read(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if (ec == beast::error::timeout)
return;
// This means they closed the connection
if (ec == http::error::end_of_stream)
return do_close();
if (ec)
return fail(ec, "read");
// Send the response
handle_request(*doc_root_, etag_cache_, parser_->release(), queue_);
// If we aren't at the queue limit, try to pipeline another request
if (!queue_.is_full())
do_read();
}
void
on_write(bool close, beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if (ec == beast::error::timeout)
return;
if (ec)
return fail(ec, "write");
if (close)
{
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return do_close();
}
// Inform the queue that a write completed
if (queue_.on_write())
{
// Read another request
do_read();
}
}
void
do_close()
{
// Send a TCP shutdown
beast::error_code ec;
stream_.socket().shutdown(tcp::socket::shutdown_send, ec);
// At this point the connection is closed gracefully
}
};
//------------------------------------------------------------------------------
// Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener>
{
net::io_context& ioc_;
tcp::acceptor acceptor_;
std::shared_ptr<std::string const> doc_root_;
etag_cache& etag_cache_;
public:
listener(
net::io_context& ioc,
tcp::endpoint endpoint,
std::shared_ptr<std::string const> const& doc_root,
etag_cache& etagCache)
: ioc_(ioc)
, acceptor_(net::make_strand(ioc))
, doc_root_(doc_root)
, etag_cache_(etagCache)
{
beast::error_code ec;
// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec)
{
fail(ec, "open");
return;
}
// // Allow address reuse
// acceptor_.set_option(net::socket_base::reuse_address(true), ec);
// if (ec)
// {
// fail(ec, "set_option");
// return;
// }
// Bind to the server address
acceptor_.bind(endpoint, ec);
if (ec)
{
fail(ec, "bind");
return;
}
// Start listening for connections
acceptor_.listen(
net::socket_base::max_listen_connections, ec);
if (ec)
{
fail(ec, "listen");
return;
}
}
// Start accepting incoming connections
void
run()
{
// We need to be executing within a strand to perform async operations
// on the I/O objects in this session. Although not strictly necessary
// for single-threaded contexts, this example code is written to be
// thread-safe by default.
net::dispatch(
acceptor_.get_executor(),
beast::bind_front_handler(
&listener::do_accept,
this->shared_from_this()));
}
private:
void
do_accept()
{
// The new connection gets its own strand
acceptor_.async_accept(
net::make_strand(ioc_),
beast::bind_front_handler(
&listener::on_accept,
shared_from_this()));
}
void
on_accept(beast::error_code ec, tcp::socket socket)
{
if (ec)
{
fail(ec, "accept");
}
else
{
// Create the http session and run it
std::make_shared<http_session>(
std::move(socket),
doc_root_,
etag_cache_)->run();
}
// Accept another connection
do_accept();
}
};
} // anonymous namespace
namespace detail
{
class CCServerImpl final
{
public:
CCServerImpl(const std::string& addressToListen, uint16_t portToListen, const std::string& docRoot, int threads)
: m_maxThreads{std::max(1, threads)}
, m_docRoot{ docRoot }
, m_etagCache{}
, m_endpoint{ boost::asio::ip::make_address(addressToListen), portToListen }
, m_ioc{ m_maxThreads }
, m_threads{}
{
}
~CCServerImpl()
{
shutdown();
}
public:
void run()
{
std::lock_guard<std::mutex> scopeGuard(m_mutex);
// Create and launch a listening port
std::make_shared<listener>(
m_ioc,
m_endpoint,
std::make_shared<std::string>(m_docRoot),
m_etagCache)->run();
// Run the I/O service on the requested number of threads
m_threads.reserve(m_maxThreads);
for (auto i = m_maxThreads - 1; i >= 0; --i)
{
m_threads.emplace_back([&]()
{
m_ioc.run();
if (i == 0)
{
m_onStopped();
}
});
}
}
void shutdown()
{
std::lock_guard<std::mutex> scopeGuard(m_mutex);
m_ioc.stop();
for (auto& t : m_threads)
{
t.join();
}
m_threads.clear();
}
bool isRunning() const
{
return m_ioc.stopped();
}
void clearCache()
{
m_etagCache.clear();
}
boost::signals2::connection connectOnStopped(const boost::signals2::signal<void()>::slot_type& slot)
{
return m_onStopped.connect(slot);
}
private:
mutable std::mutex m_mutex;
int m_maxThreads;
std::string m_docRoot;
etag_cache m_etagCache;
tcp::endpoint m_endpoint;
boost::asio::io_context m_ioc;
std::vector<std::thread> m_threads;
boost::signals2::signal<void()> m_onStopped;
};
}
CCServer::CCServer(const std::string& addressToListen, uint16_t portToListen, const std::string& docRoot, int threads)
: m_impl(std::make_unique<detail::CCServerImpl>(addressToListen, portToListen, docRoot, threads))
{}
CCServer::~CCServer()
{}
void CCServer::shutdown()
{
m_impl->shutdown();
}
void CCServer::clearCache()
{
m_impl->clearCache();
}
boost::signals2::connection CCServer::connectOnStopped(const boost::signals2::signal<void()>::slot_type & slot)
{
return m_impl->connectOnStopped(slot);
}
void CCServer::run()
{
m_impl->run();
}
bool CCServer::isRunning() const
{
return m_impl->isRunning();
}

27
CMakeProject1/CCServer.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <boost/signals2.hpp>
#include <memory>
#include <string>
namespace detail
{
class CCServerImpl;
}
class CCServer final
{
public:
CCServer(const std::string& addressToListen, uint16_t portToListen, const std::string& docRoot, int threads);
~CCServer();
public:
void run();
bool isRunning() const;
void shutdown();
void clearCache();
boost::signals2::connection connectOnStopped(const boost::signals2::signal<void()>::slot_type& slot);
private:
std::unique_ptr<detail::CCServerImpl> m_impl;
};

View File

@ -0,0 +1,30 @@
# CMakeList.txt : CMake project for CMakeProject1, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)
# Download automatically, you can also just copy the conan.cmake file
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.16.1/conan.cmake"
"${CMAKE_BINARY_DIR}/conan.cmake"
EXPECTED_HASH SHA256=396e16d0f5eabdc6a14afddbcfff62a54a7ee75c6da23f32f7a31bc85db23484
TLS_VERIFY ON)
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include(${CMAKE_BINARY_DIR}/conan.cmake)
conan_cmake_run(CONANFILE conanfile.txt
BUILD missing
BASIC_SETUP)
# Add source to this project's executable.
add_executable (CMakeProject1 "CCServer.cpp" "CCServer.h" "CryptoUtils.cpp" "CryptoUtils.h" "CMakeProject1.cpp" "CMakeProject1.h")
target_link_libraries(CMakeProject1 ${CONAN_LIBS})
# TODO: Add tests and install targets if needed.

View File

@ -0,0 +1,62 @@
#include "CCServer.h"
#include <boost/program_options.hpp>
#include <boost/asio/signal_set.hpp>
#include <iostream>
#include <memory>
#include <chrono>
#include <thread>
namespace po = boost::program_options;
namespace net = boost::asio; // from <boost/asio.hpp>
volatile bool signalCaught = false;
void signalHandler(int signum) {
std::cout << "Interrupt signal (" << signum << ") received.\n";
signalCaught = true;
}
int main(int argc, const char* argv[])
{
// Declare the supported options.
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("bind_ip", po::value<std::string>()->default_value("0.0.0.0"), "ip address to listen for connections")
("port", po::value<uint16_t>()->default_value(8080), "port")
("data_root", po::value<std::string>()->default_value("."), "root directory with data")
("threads", po::value<int>()->default_value(4), "port")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << "\n";
return 1;
}
if (vm.count("port")) {
std::cout << "Port "
<< vm["port"].as<uint16_t>() << ".\n";
}
auto addressToListen = vm["bind_ip"].as<std::string>();
auto portToListen = vm["port"].as<uint16_t>();
auto docRoot = vm["data_root"].as<std::string>();
auto threads = vm["threads"].as<int>();
auto ccServer = std::make_unique<CCServer>(addressToListen, portToListen, docRoot, threads);
ccServer->run();
while (!signalCaught)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
ccServer->shutdown();
return 0;
}

View File

@ -0,0 +1,8 @@
// CMakeProject1.h : Include file for standard system include files,
// or project specific include files.
#pragma once
#include <iostream>
// TODO: Reference additional headers your program requires here.

View File

@ -0,0 +1,92 @@
#include "CryptoUtils.h"
#include <cryptopp/base64.h>
#include <cryptopp/base32.h>
using CryptoPP::Base64Decoder;
using CryptoPP::Base64Encoder;
using CryptoPP::Base32Decoder;
using CryptoPP::Base32Encoder;
using CryptoPP::StringSource;
using CryptoPP::StringSink;
using CryptoPP::byte;
namespace
{
static const byte base32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
}
std::string base64Decode(const std::string& encoded)
{
std::string decoded;
StringSource ss(encoded, true,
new Base64Decoder(
new StringSink(decoded)
)
);
return decoded;
}
std::string base64Encode(const std::string& source)
{
std::string encoded;
StringSource ss(source, true,
new Base64Encoder(
new StringSink(encoded),
false /* no line breaks */
)
);
return encoded;
}
std::unique_ptr<uint8_t[]> base64Encode(const uint8_t* source, size_t sourceSize)
{
auto encoded = std::make_unique<uint8_t[]>((sourceSize*4/3 + 3) & ~3);
CryptoPP::ArraySource ss(source, sourceSize, true,
new Base64Encoder(
new CryptoPP::ArraySink(encoded.get(), sourceSize * 4 / 3),
false /* no line breaks */
)
);
return encoded;
}
std::string base32Decode(const std::string& encoded)
{
// Decoder
int lookup[256] = { 0 };
Base64Decoder::InitializeDecodingLookupArray(lookup, base32Alphabet, 32, true);
Base32Decoder decoder;
CryptoPP::AlgorithmParameters params = CryptoPP::MakeParameters(CryptoPP::Name::DecodingLookupArray(), (const int *)lookup);
decoder.IsolatedInitialize(params);
std::string decoded;
decoder.Attach(new StringSink(decoded));
decoder.Put((const byte*)encoded.data(), encoded.size());
decoder.MessageEnd();
return decoded;
}
std::string base32Encode(const std::string& data)
{
// Encoder
Base32Encoder encoder;
CryptoPP::AlgorithmParameters params = CryptoPP::MakeParameters(CryptoPP::Name::EncodingLookupArray(), (const byte *)base32Alphabet);
encoder.IsolatedInitialize(params);
std::string encoded;
encoder.Attach(new StringSink(encoded));
encoder.Put((const byte*)data.data(), data.size());
encoder.MessageEnd();
return encoded;
}

View File

@ -0,0 +1,10 @@
#pragma once
#include <string>
#include <memory>
std::string base64Decode(const std::string& encoded);
std::string base64Encode(const std::string& source);
std::unique_ptr<uint8_t[]> base64Encode(const uint8_t * source, size_t sourceSize);
std::string base32Decode(const std::string& encoded);
std::string base32Encode(const std::string& data);

180
CMakeProject1/Makefile Normal file
View File

@ -0,0 +1,180 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.16
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
.SUFFIXES: .hpux_make_needs_suffix_list
# Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
# Set environment variables for the build.
# The shell in which to execute make rules.
SHELL = /bin/sh
# The CMake executable.
CMAKE_COMMAND = /usr/bin/cmake
# The command to remove a file.
RM = /usr/bin/cmake -E remove -f
# Escaping for special characters.
EQUALS = =
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /mnt/c/Users/petos/source/repos/CMakeProject1
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /mnt/c/Users/petos/source/repos/CMakeProject1
#=============================================================================
# Targets provided globally by CMake.
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
/usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
/usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# The main all target
all: cmake_check_build_system
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(CMAKE_COMMAND) -E cmake_progress_start /mnt/c/Users/petos/source/repos/CMakeProject1/CMakeFiles /mnt/c/Users/petos/source/repos/CMakeProject1/CMakeProject1/CMakeFiles/progress.marks
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/all
$(CMAKE_COMMAND) -E cmake_progress_start /mnt/c/Users/petos/source/repos/CMakeProject1/CMakeFiles 0
.PHONY : all
# The main clean target
clean:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/preinstall
.PHONY : preinstall/fast
# clear depends
depend:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend
# Convenience name for target.
CMakeProject1/CMakeFiles/CMakeProject1.dir/rule:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeFiles/Makefile2 CMakeProject1/CMakeFiles/CMakeProject1.dir/rule
.PHONY : CMakeProject1/CMakeFiles/CMakeProject1.dir/rule
# Convenience name for target.
CMakeProject1: CMakeProject1/CMakeFiles/CMakeProject1.dir/rule
.PHONY : CMakeProject1
# fast build rule for target.
CMakeProject1/fast:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/build
.PHONY : CMakeProject1/fast
CMakeProject1.o: CMakeProject1.cpp.o
.PHONY : CMakeProject1.o
# target to build an object file
CMakeProject1.cpp.o:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/CMakeProject1.cpp.o
.PHONY : CMakeProject1.cpp.o
CMakeProject1.i: CMakeProject1.cpp.i
.PHONY : CMakeProject1.i
# target to preprocess a source file
CMakeProject1.cpp.i:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/CMakeProject1.cpp.i
.PHONY : CMakeProject1.cpp.i
CMakeProject1.s: CMakeProject1.cpp.s
.PHONY : CMakeProject1.s
# target to generate assembly for a file
CMakeProject1.cpp.s:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(MAKE) -f CMakeProject1/CMakeFiles/CMakeProject1.dir/build.make CMakeProject1/CMakeFiles/CMakeProject1.dir/CMakeProject1.cpp.s
.PHONY : CMakeProject1.cpp.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@echo "... all (the default if no target is provided)"
@echo "... clean"
@echo "... depend"
@echo "... rebuild_cache"
@echo "... edit_cache"
@echo "... CMakeProject1"
@echo "... CMakeProject1.o"
@echo "... CMakeProject1.i"
@echo "... CMakeProject1.s"
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
cd /mnt/c/Users/petos/source/repos/CMakeProject1 && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system

View File

@ -0,0 +1,10 @@
[requires]
boost/1.76.0
cryptopp/8.5.0
[generators]
cmake
[options]
boost:shared=False
cryptopp:shared=False

88
CMakeSettings.json Normal file
View File

@ -0,0 +1,88 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"variables": [
{
"name": "CMAKE_SYSTEM_VERSION",
"value": "8.1",
"type": "STRING"
}
]
},
{
"name": "WSL-GCC-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"wslPath": "${defaultWSLPath}"
},
{
"name": "WSL-GCC-Release",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"wslPath": "${defaultWSLPath}",
"variables": [
{
"name": "CMAKE_CXX_FLAGS_RELEASE",
"value": "-O3 -DNDEBUG -DBOOST_EXCEPTION_DISABLE=1 -DBOOST_NO_RTTI=1 -flto",
"type": "STRING"
},
{
"name": "CMAKE_EXE_LINKER_FLAGS_RELEASE",
"value": "-s -flto -Wl,-rpath=.",
"type": "STRING"
}
]
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": [
{
"name": "CMAKE_SYSTEM_VERSION",
"value": "8.1",
"type": "STRING"
},
{
"name": "CMAKE_EXE_LINKER_FLAGS_RELEASE",
"value": "/INCREMENTAL:NO /ASSEMBLYDEBUG:DISABLE /OPT:REF /DEBUG:NONE /PDBSTRIPPED:YES",
"type": "STRING"
},
{
"name": "CMAKE_CXX_FLAGS_RELEASE",
"value": "/MD /O2 /Ob2 /DNDEBUG /DBOOST_EXCEPTION_DISABLE=1",
"type": "STRING"
}
]
}
]
}