Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 526b27deaf | |||
| 7c19123da9 | |||
| c9e1b0a149 | |||
| 0d96c21199 | |||
| 679d752177 | |||
| 9f88390b1a | |||
| a4c84379ff | |||
| 827cc07175 |
@@ -2,3 +2,4 @@
|
|||||||
.xmake
|
.xmake
|
||||||
build
|
build
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
ignore
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "portscan_cli.hpp"
|
||||||
|
#include <boost/program_options/value_semantic.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
PortScanCli::PortScanCli()
|
||||||
|
: CliBase("Port Scanner Options")
|
||||||
|
, ip_("")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanCli::setupOptions()
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
desc_.add_options()
|
||||||
|
("help,h", "Show help")
|
||||||
|
("ip,H", withDefault(ip_, "<IP>"), "Local IPv4 to scan");
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PortScanCli::getIp() const
|
||||||
|
{
|
||||||
|
if (vm_["ip"].empty() == 0) {
|
||||||
|
return "127.0.0.1";
|
||||||
|
}
|
||||||
|
return vm_.at("ip").as<std::string>();
|
||||||
|
}
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cli_base.hpp"
|
#include "cli_base.hpp"
|
||||||
|
#include "option_utils.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class DnsCli : public CliBase
|
class PortScanCli : public CliBase, protected OptionUtils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DnsCli();
|
PortScanCli();
|
||||||
|
|
||||||
int getPort() const;
|
|
||||||
std::string getIp() const;
|
std::string getIp() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setupOptions() override;
|
void setupOptions() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int port_;
|
|
||||||
std::string ip_;
|
std::string ip_;
|
||||||
};
|
};
|
||||||
@@ -1,27 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
// --- Error handling ---
|
// --- Error handling ---
|
||||||
using ErrorCode = boost::system::error_code;
|
using ErrorCode = boost::system::error_code;
|
||||||
|
|
||||||
// --- IO Context ---
|
// --- Asio types ---
|
||||||
using IoContext = boost::asio::io_context;
|
using IoContext = boost::asio::io_context;
|
||||||
|
using SteadyTimer = boost::asio::steady_timer;
|
||||||
|
|
||||||
// --- TCP ---
|
// --- Ip types ---
|
||||||
|
using IpAddress = boost::asio::ip::address;
|
||||||
|
|
||||||
|
// --- TCP types ---
|
||||||
using TcpSocket = boost::asio::ip::tcp::socket;
|
using TcpSocket = boost::asio::ip::tcp::socket;
|
||||||
using TcpAcceptor = boost::asio::ip::tcp::acceptor;
|
using TcpAcceptor = boost::asio::ip::tcp::acceptor;
|
||||||
using TcpEndpoint = boost::asio::ip::tcp::endpoint;
|
using TcpEndpoint = boost::asio::ip::tcp::endpoint;
|
||||||
|
|
||||||
// --- UDP (для DNS) ---
|
// --- UDP types ---
|
||||||
using UdpSocket = boost::asio::ip::udp::socket;
|
using UdpSocket = boost::asio::ip::udp::socket;
|
||||||
using UdpEndpoint = boost::asio::ip::udp::endpoint;
|
using UdpEndpoint = boost::asio::ip::udp::endpoint;
|
||||||
|
|
||||||
// --- Таймеры ---
|
|
||||||
using SteadyTimer = boost::asio::steady_timer;
|
|
||||||
|
|
||||||
// --- Program options ---
|
// --- Program options ---
|
||||||
using OptionsDescription = boost::program_options::options_description;
|
using OptionsDescription = boost::program_options::options_description;
|
||||||
using VariablesMap = boost::program_options::variables_map;
|
using VariablesMap = boost::program_options::variables_map;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
|
|
||||||
#include <boost/date_time/posix_time/posix_time_io.hpp>
|
#include <boost/date_time/posix_time/posix_time_io.hpp>
|
||||||
|
|
||||||
void Logger::init(bool toConsole, const std::string_view filePath)
|
void Logger::init(bool toConsole, const std::string_view filePath)
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
class OptionUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using OptionValue = boost::program_options::typed_value<T> *;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static OptionValue<T>
|
||||||
|
withDefault(const T &value, const char *name = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
OptionUtils() = default;
|
||||||
|
~OptionUtils() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "option_utils.tpp"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "option_utils.hpp"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
OptionUtils::OptionValue<T>
|
||||||
|
OptionUtils::withDefault(const T &value, const char *name)
|
||||||
|
{
|
||||||
|
auto v = boost::program_options::value<T>()->default_value(value);
|
||||||
|
if (name) {
|
||||||
|
v->value_name(name); // Optional
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
class Server
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~Server() = default;
|
|
||||||
virtual void start() = 0;
|
|
||||||
virtual void stop() = 0;
|
|
||||||
};
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#include "session_base.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
#include <boost/asio/write.hpp>
|
|
||||||
|
|
||||||
SessionBase::SessionBase(TcpSocket socket)
|
|
||||||
: socket_(std::move(socket))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionBase::start()
|
|
||||||
{
|
|
||||||
doRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionBase::doRead()
|
|
||||||
{
|
|
||||||
using namespace boost::asio;
|
|
||||||
|
|
||||||
auto self = shared_from_this();
|
|
||||||
|
|
||||||
auto handler = [this, self](ErrorCode ec, std::size_t bytesTransferred) {
|
|
||||||
onRead(ec, bytesTransferred);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket_.async_read_some(buffer(buffer_), handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionBase::onRead(ErrorCode ec, std::size_t bytesTransferred)
|
|
||||||
{
|
|
||||||
if (ec) {
|
|
||||||
Logger::error("Read error: " + ec.message());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string rawRequest(buffer_.data(), bytesTransferred);
|
|
||||||
Logger::info("Received request:\n" + rawRequest);
|
|
||||||
|
|
||||||
try {
|
|
||||||
handleRequest(rawRequest);
|
|
||||||
} catch (const std::exception &ex) {
|
|
||||||
Logger::error(std::string("Request handling failed: ") + ex.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionBase::doWrite(const std::string &response)
|
|
||||||
{
|
|
||||||
using namespace boost::asio;
|
|
||||||
|
|
||||||
auto self = shared_from_this();
|
|
||||||
auto onWriteHandler =
|
|
||||||
[this, self](ErrorCode ec, std::size_t /*bytesTransferred*/) {
|
|
||||||
if (ec) {
|
|
||||||
Logger::error("Write error: " + ec.message());
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode shutdownEc;
|
|
||||||
if (socket_.shutdown(TcpSocket::shutdown_both, shutdownEc)) {
|
|
||||||
Logger::warn("Shutdown error: " + shutdownEc.message());
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorCode closeEc;
|
|
||||||
if (socket_.close(closeEc)) {
|
|
||||||
Logger::warn("Close error: " + closeEc.message());
|
|
||||||
} else {
|
|
||||||
Logger::info("Connection closed");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async_write(socket_, buffer(response), onWriteHandler);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aliases.hpp"
|
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class SessionBase : public std::enable_shared_from_this<SessionBase>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit SessionBase(TcpSocket socket);
|
|
||||||
virtual ~SessionBase() = default;
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void handleRequest(const std::string_view rawRequest) = 0;
|
|
||||||
|
|
||||||
void doRead();
|
|
||||||
void onRead(ErrorCode ec, std::size_t bytesTransferred);
|
|
||||||
void doWrite(const std::string &response);
|
|
||||||
|
|
||||||
TcpSocket socket_;
|
|
||||||
std::array<char, 8192> buffer_;
|
|
||||||
};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#include "tcp_server_base.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
TcpServerBase::TcpServerBase(IoContext &io, unsigned short port)
|
|
||||||
: acceptor_(io, TcpEndpoint(boost::asio::ip::tcp::v4(), port))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcpServerBase::start()
|
|
||||||
{
|
|
||||||
auto doAccept = [this]() {
|
|
||||||
acceptor_.async_accept([this](ErrorCode ec, TcpSocket socket) {
|
|
||||||
if (!ec) {
|
|
||||||
Logger::info("New client connected");
|
|
||||||
createSession(std::move(socket));
|
|
||||||
} else {
|
|
||||||
Logger::error("Accept error: " + ec.message());
|
|
||||||
}
|
|
||||||
start(); // continue accepting
|
|
||||||
});
|
|
||||||
};
|
|
||||||
doAccept();
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aliases.hpp"
|
|
||||||
|
|
||||||
class TcpServerBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TcpServerBase(IoContext &io, unsigned short port);
|
|
||||||
virtual ~TcpServerBase() = default;
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void createSession(TcpSocket socket) = 0;
|
|
||||||
|
|
||||||
TcpAcceptor acceptor_;
|
|
||||||
};
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#include "udp_server_base.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
UdpServerBase::UdpServerBase(IoContext &io, unsigned short port)
|
|
||||||
: socket_(io, UdpEndpoint(boost::asio::ip::udp::v4(), port))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void UdpServerBase::start()
|
|
||||||
{
|
|
||||||
doReceive();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UdpServerBase::doReceive()
|
|
||||||
{
|
|
||||||
using namespace boost::asio;
|
|
||||||
|
|
||||||
auto onReceive = [this](ErrorCode ec, std::size_t bytesReceived) {
|
|
||||||
if (!ec && bytesReceived > 0) {
|
|
||||||
std::string_view data(buffer_.data(), bytesReceived);
|
|
||||||
handleRequest(data, remoteEndpoint_);
|
|
||||||
} else if (ec) {
|
|
||||||
Logger::error("UDP receive error: " + ec.message());
|
|
||||||
}
|
|
||||||
doReceive(); // continue listening
|
|
||||||
};
|
|
||||||
|
|
||||||
socket_.async_receive_from(buffer(buffer_), remoteEndpoint_, onReceive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UdpServerBase::doSend(
|
|
||||||
const std::string &response, const UdpEndpoint &target
|
|
||||||
)
|
|
||||||
{
|
|
||||||
using namespace boost::asio;
|
|
||||||
|
|
||||||
auto bufferToSend = std::make_shared<std::string>(response);
|
|
||||||
|
|
||||||
auto onSend = [bufferToSend](ErrorCode ec, std::size_t /*bytesSent*/) {
|
|
||||||
if (ec) {
|
|
||||||
Logger::error("UDP send error: " + ec.message());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
socket_.async_send_to(buffer(*bufferToSend), target, onSend);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aliases.hpp"
|
|
||||||
|
|
||||||
class UdpServerBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
UdpServerBase(IoContext &io, unsigned short port);
|
|
||||||
virtual ~UdpServerBase() = default;
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void
|
|
||||||
handleRequest(const std::string_view data, const UdpEndpoint &sender) = 0;
|
|
||||||
|
|
||||||
void doReceive();
|
|
||||||
void doSend(const std::string &response, const UdpEndpoint &target);
|
|
||||||
|
|
||||||
UdpSocket socket_;
|
|
||||||
std::array<char, 4096> buffer_;
|
|
||||||
UdpEndpoint remoteEndpoint_;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#include "port_scanner.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
PortScanner::PortScanner(
|
||||||
|
const std::string &ip, const std::vector<uint16_t> &ports
|
||||||
|
)
|
||||||
|
: ports_(ports)
|
||||||
|
{
|
||||||
|
ErrorCode ec;
|
||||||
|
addr_ = boost::asio::ip::make_address(ip, ec);
|
||||||
|
ip_ok_ = !ec;
|
||||||
|
|
||||||
|
if (!ip_ok_) {
|
||||||
|
Logger::error(std::string("PortScanner: invalid IP: ") + ip);
|
||||||
|
} else {
|
||||||
|
Logger::debug(std::string("PortScanner: using IP: ") + ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::setTimeoutMs(int ms)
|
||||||
|
{
|
||||||
|
if (ms > 0) {
|
||||||
|
timeout_ms_ = ms;
|
||||||
|
Logger::debug(
|
||||||
|
std::string("PortScanner: timeout set to ")
|
||||||
|
+ std::to_string(timeout_ms_) + " ms"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::run()
|
||||||
|
{
|
||||||
|
if (!ip_ok_) {
|
||||||
|
Logger::error("PortScanner::run - aborting: ip not ok");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (uint16_t p : ports_) {
|
||||||
|
if (probe(p)) {
|
||||||
|
Logger::info(std::to_string(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PortScanner::probe(uint16_t port)
|
||||||
|
{
|
||||||
|
TcpSocket s(io_);
|
||||||
|
SteadyTimer t(io_);
|
||||||
|
auto ep = endpoint(port);
|
||||||
|
|
||||||
|
Logger::debug(
|
||||||
|
std::string("PortScanner::probe - probing port ") + std::to_string(port)
|
||||||
|
);
|
||||||
|
|
||||||
|
armTimer(t, s);
|
||||||
|
armConnect(s, ep);
|
||||||
|
runUntilDone();
|
||||||
|
cancelTimer(t);
|
||||||
|
drainLeftovers();
|
||||||
|
close(s);
|
||||||
|
|
||||||
|
if (ok_) {
|
||||||
|
Logger::debug(std::string("Port ") + std::to_string(port) + " is OPEN");
|
||||||
|
} else {
|
||||||
|
Logger::debug(
|
||||||
|
std::string("Port ") + std::to_string(port) + " is CLOSED/FILTERED"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok_;
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpEndpoint PortScanner::endpoint(uint16_t port) const
|
||||||
|
{
|
||||||
|
return TcpEndpoint(addr_, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::armTimer(SteadyTimer &t, TcpSocket &s)
|
||||||
|
{
|
||||||
|
t.expires_after(std::chrono::milliseconds(timeout_ms_));
|
||||||
|
|
||||||
|
auto onTimeout = [&s](const ErrorCode &ec) {
|
||||||
|
if (!ec) {
|
||||||
|
Logger::debug("Timer expired -> cancelling socket");
|
||||||
|
s.cancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
t.async_wait(onTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::armConnect(TcpSocket &s, const TcpEndpoint &ep)
|
||||||
|
{
|
||||||
|
ok_ = false;
|
||||||
|
done_ = false;
|
||||||
|
|
||||||
|
auto onConnect = [this](const ErrorCode &ec) {
|
||||||
|
if (!ec) {
|
||||||
|
ok_ = true;
|
||||||
|
Logger::debug("async_connect succeeded");
|
||||||
|
} else {
|
||||||
|
ok_ = false;
|
||||||
|
Logger::debug(std::string("async_connect failed: ") + ec.message());
|
||||||
|
}
|
||||||
|
done_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
s.async_connect(ep, onConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::runUntilDone()
|
||||||
|
{
|
||||||
|
io_.restart();
|
||||||
|
while (!done_ && io_.run_one()) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::cancelTimer(SteadyTimer &t)
|
||||||
|
{
|
||||||
|
ErrorCode ec;
|
||||||
|
t.cancel();
|
||||||
|
if (ec) {
|
||||||
|
Logger::warn(std::string("cancelTimer: ") + ec.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::drainLeftovers()
|
||||||
|
{
|
||||||
|
io_.restart();
|
||||||
|
while (io_.run_one()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortScanner::close(TcpSocket &s)
|
||||||
|
{
|
||||||
|
ErrorCode ig;
|
||||||
|
if (s.close(ig)) {
|
||||||
|
Logger::warn(std::string("close socket failed: ") + ig.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "aliases.hpp"
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class PortScanner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PortScanner(const std::string &ip, const std::vector<uint16_t> &ports);
|
||||||
|
|
||||||
|
void setTimeoutMs(int ms);
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool probe(uint16_t port);
|
||||||
|
TcpEndpoint endpoint(uint16_t port) const;
|
||||||
|
|
||||||
|
void armTimer(SteadyTimer &t, TcpSocket &s);
|
||||||
|
void armConnect(TcpSocket &s, const TcpEndpoint &ep);
|
||||||
|
void runUntilDone();
|
||||||
|
void cancelTimer(SteadyTimer &t);
|
||||||
|
void drainLeftovers();
|
||||||
|
void close(TcpSocket &s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IoContext io_;
|
||||||
|
IpAddress addr_;
|
||||||
|
std::vector<uint16_t> ports_;
|
||||||
|
|
||||||
|
int timeout_ms_{300};
|
||||||
|
bool ip_ok_{false};
|
||||||
|
bool ok_{false};
|
||||||
|
bool done_{false};
|
||||||
|
};
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#include "dns_cli.hpp"
|
|
||||||
|
|
||||||
// helper for default values
|
|
||||||
constexpr auto withDefault = [](auto value) {
|
|
||||||
using T = decltype(value);
|
|
||||||
return boost::program_options::value<T>()->default_value(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
DnsCli::DnsCli()
|
|
||||||
: CliBase("DNS Server Options")
|
|
||||||
, port_(5300)
|
|
||||||
, ip_("127.0.0.1")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DnsCli::setupOptions()
|
|
||||||
{
|
|
||||||
// clang-format off
|
|
||||||
desc_.add_options()
|
|
||||||
("help,h", "Show help message")
|
|
||||||
("port,p", withDefault(5300), "UDP port to listen on")
|
|
||||||
("ip,i", withDefault(std::string("127.0.0.1")), "IP address to return in A record");
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
int DnsCli::getPort() const
|
|
||||||
{
|
|
||||||
return vm_["port"].as<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DnsCli::getIp() const
|
|
||||||
{
|
|
||||||
return vm_["ip"].as<std::string>();
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#include "dns_server.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
DnsServer::DnsServer(IoContext &io, unsigned short port, std::string answerIp)
|
|
||||||
: UdpServerBase(io, port)
|
|
||||||
, answerIp_(std::move(answerIp))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DnsServer::handleRequest(
|
|
||||||
const std::string_view data, const UdpEndpoint &sender
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (data.size() < 12) {
|
|
||||||
Logger::warn("Invalid DNS packet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// response buffer
|
|
||||||
std::string resp;
|
|
||||||
|
|
||||||
// copy ID
|
|
||||||
resp.append(data.substr(0, 2));
|
|
||||||
|
|
||||||
// flags: QR=1 (response), AA=1
|
|
||||||
resp.push_back('\x81');
|
|
||||||
resp.push_back('\x80');
|
|
||||||
|
|
||||||
// QDCOUNT=1, ANCOUNT=1, NSCOUNT=0, ARCOUNT=0
|
|
||||||
resp.append("\0\1\0\1\0\0\0\0", 8);
|
|
||||||
|
|
||||||
// copy question (from offset 12 till end of query)
|
|
||||||
resp.append(data.substr(12));
|
|
||||||
|
|
||||||
// answer section
|
|
||||||
resp.append("\xc0\x0c"); // name pointer to question
|
|
||||||
resp.append("\0\1"); // TYPE A
|
|
||||||
resp.append("\0\1"); // CLASS IN
|
|
||||||
resp.append("\0\0\0\x3c"); // TTL = 60
|
|
||||||
resp.append("\0\x04"); // RDLENGTH = 4
|
|
||||||
|
|
||||||
// answer IP (from CLI or default 127.0.0.1)
|
|
||||||
unsigned int b1, b2, b3, b4;
|
|
||||||
if (sscanf(answerIp_.c_str(), "%u.%u.%u.%u", &b1, &b2, &b3, &b4) != 4) {
|
|
||||||
b1 = 127;
|
|
||||||
b2 = 0;
|
|
||||||
b3 = 0;
|
|
||||||
b4 = 1;
|
|
||||||
}
|
|
||||||
resp.push_back(static_cast<char>(b1));
|
|
||||||
resp.push_back(static_cast<char>(b2));
|
|
||||||
resp.push_back(static_cast<char>(b3));
|
|
||||||
resp.push_back(static_cast<char>(b4));
|
|
||||||
|
|
||||||
Logger::info("DNS query answered with A " + answerIp_);
|
|
||||||
doSend(resp, sender);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "udp_server_base.hpp"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class DnsServer : public UdpServerBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DnsServer(IoContext &io, unsigned short port, std::string answerIp);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void handleRequest(
|
|
||||||
const std::string_view data, const UdpEndpoint &sender
|
|
||||||
) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string answerIp_;
|
|
||||||
};
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#include "file_cli.hpp"
|
|
||||||
|
|
||||||
// helper for default values
|
|
||||||
constexpr auto withDefault = [](auto value) {
|
|
||||||
using T = decltype(value);
|
|
||||||
return boost::program_options::value<T>()->default_value(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
FileCli::FileCli()
|
|
||||||
: CliBase("File Server Options")
|
|
||||||
, port_(9090)
|
|
||||||
, rootDir_("./data")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileCli::setupOptions()
|
|
||||||
{
|
|
||||||
// clang-format off
|
|
||||||
desc_.add_options()
|
|
||||||
("help,h", "Show help message")
|
|
||||||
("port,p", withDefault(9090), "Port to listen on")
|
|
||||||
("root,r", withDefault(std::string("./data")), "Root directory for files");
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileCli::getPort() const
|
|
||||||
{
|
|
||||||
return vm_["port"].as<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FileCli::getRootDir() const
|
|
||||||
{
|
|
||||||
return vm_["root"].as<std::string>();
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cli_base.hpp"
|
|
||||||
|
|
||||||
class FileCli : public CliBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileCli();
|
|
||||||
|
|
||||||
int getPort() const;
|
|
||||||
std::string getRootDir() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setupOptions() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int port_;
|
|
||||||
std::string rootDir_;
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#include "file_server.hpp"
|
|
||||||
#include "file_session.hpp"
|
|
||||||
|
|
||||||
FileServer::FileServer(IoContext &io, unsigned short port, std::string rootDir)
|
|
||||||
: TcpServerBase(io, port)
|
|
||||||
, rootDir_(std::move(rootDir))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileServer::createSession(TcpSocket socket)
|
|
||||||
{
|
|
||||||
std::make_shared<FileSession>(std::move(socket), rootDir_)->start();
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "tcp_server_base.hpp"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class FileServer : public TcpServerBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileServer(IoContext &io, unsigned short port, std::string rootDir);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void createSession(TcpSocket socket) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string rootDir_;
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#include "file_session.hpp"
|
|
||||||
#include "session_base.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
FileSession::FileSession(TcpSocket socket, std::string rootDir)
|
|
||||||
: SessionBase(std::move(socket))
|
|
||||||
, rootDir_(std::move(rootDir))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSession::handleRequest(const std::string_view rawRequest)
|
|
||||||
{
|
|
||||||
// strip trailing newlines
|
|
||||||
std::string fileName(rawRequest);
|
|
||||||
while (!fileName.empty()
|
|
||||||
&& (fileName.back() == '\r' || fileName.back() == '\n')) {
|
|
||||||
fileName.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filePath = rootDir_ + "/" + fileName;
|
|
||||||
std::ifstream file(filePath, std::ios::binary);
|
|
||||||
|
|
||||||
std::ostringstream ss;
|
|
||||||
if (file) {
|
|
||||||
ss << file.rdbuf();
|
|
||||||
Logger::info("File served: " + filePath);
|
|
||||||
} else {
|
|
||||||
ss << "File not found: " << fileName;
|
|
||||||
Logger::warn("File not found: " + filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
doWrite(ss.str());
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "session_base.hpp"
|
|
||||||
|
|
||||||
class FileSession : public SessionBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileSession(TcpSocket socket, std::string rootDir);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void handleRequest(const std::string_view rawRequest) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string rootDir_;
|
|
||||||
};
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#include "http_cli.hpp"
|
|
||||||
|
|
||||||
// helper for default values
|
|
||||||
constexpr auto withDefault = [](auto value) {
|
|
||||||
using T = decltype(value);
|
|
||||||
return boost::program_options::value<T>()->default_value(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpCli::HttpCli()
|
|
||||||
: CliBase("HTTP Server Options")
|
|
||||||
, port_(8080)
|
|
||||||
, rootDir_("./www")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpCli::setupOptions()
|
|
||||||
{
|
|
||||||
// clang-format off
|
|
||||||
desc_.add_options()
|
|
||||||
("help,h", "Show help message")
|
|
||||||
("port,p", withDefault(8080), "Port to listen on")
|
|
||||||
("root,r", withDefault(std::string("./www")), "Root directory for files");
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
int HttpCli::getPort() const
|
|
||||||
{
|
|
||||||
return vm_["port"].as<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HttpCli::getRootDir() const
|
|
||||||
{
|
|
||||||
return vm_["root"].as<std::string>();
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cli_base.hpp"
|
|
||||||
|
|
||||||
class HttpCli : public CliBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HttpCli();
|
|
||||||
|
|
||||||
int getPort() const;
|
|
||||||
std::string getRootDir() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void setupOptions() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int port_;
|
|
||||||
std::string rootDir_;
|
|
||||||
};
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#include "http_request.hpp"
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
void HttpRequest::setMethod(const std::string_view method)
|
|
||||||
{
|
|
||||||
method_ = method;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string HttpRequest::getMethod() const
|
|
||||||
{
|
|
||||||
return method_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpRequest::setPath(const std::string_view path)
|
|
||||||
{
|
|
||||||
path_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string HttpRequest::getPath() const
|
|
||||||
{
|
|
||||||
return path_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpRequest::setVersion(const std::string_view version)
|
|
||||||
{
|
|
||||||
version_ = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string HttpRequest::getVersion() const
|
|
||||||
{
|
|
||||||
return version_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpRequest::addHeader(
|
|
||||||
const std::string_view key, const std::string_view value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
headers_[std::string(key)] = std::string(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HttpRequest::hasHeader(const std::string_view key) const
|
|
||||||
{
|
|
||||||
return headers_.find(std::string(key)) != headers_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HttpRequest::getHeader(const std::string_view key) const
|
|
||||||
{
|
|
||||||
auto it = headers_.find(std::string(key));
|
|
||||||
if (it == headers_.end()) {
|
|
||||||
throw std::runtime_error("Header not found: " + std::string(key));
|
|
||||||
}
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::unordered_map<std::string, std::string> &
|
|
||||||
HttpRequest::getHeaders() const
|
|
||||||
{
|
|
||||||
return headers_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpRequest::setBody(const std::string_view body)
|
|
||||||
{
|
|
||||||
body_ = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string HttpRequest::getBody() const
|
|
||||||
{
|
|
||||||
return body_;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class HttpRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HttpRequest() = default;
|
|
||||||
|
|
||||||
void setMethod(const std::string_view method);
|
|
||||||
const std::string getMethod() const;
|
|
||||||
|
|
||||||
void setPath(const std::string_view path);
|
|
||||||
const std::string getPath() const;
|
|
||||||
|
|
||||||
void setVersion(const std::string_view version);
|
|
||||||
const std::string getVersion() const;
|
|
||||||
|
|
||||||
void addHeader(const std::string_view key, const std::string_view value);
|
|
||||||
bool hasHeader(const std::string_view key) const;
|
|
||||||
std::string getHeader(const std::string_view key) const;
|
|
||||||
const std::unordered_map<std::string, std::string> &getHeaders() const;
|
|
||||||
|
|
||||||
void setBody(const std::string_view body);
|
|
||||||
const std::string getBody() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string method_; // GET, POST
|
|
||||||
std::string path_; // /index.html
|
|
||||||
std::string version_; // HTTP/1.1
|
|
||||||
std::unordered_map<std::string, std::string> headers_;
|
|
||||||
std::string body_;
|
|
||||||
};
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
#include "http_request_parser.hpp"
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
HttpRequest HttpRequestParser::parse(const std::string &raw)
|
|
||||||
{
|
|
||||||
std::istringstream stream(raw);
|
|
||||||
HttpRequest request;
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
if (!std::getline(stream, line)) {
|
|
||||||
throw std::runtime_error("Invalid HTTP request: empty");
|
|
||||||
}
|
|
||||||
if (!line.empty() && line.back() == '\r') {
|
|
||||||
line.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
parseRequestLine(line, request);
|
|
||||||
parseHeaders(stream, request);
|
|
||||||
parseBody(stream, request);
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse "GET /index.html HTTP/1.1"
|
|
||||||
void HttpRequestParser::parseRequestLine(
|
|
||||||
const std::string &line, HttpRequest &req
|
|
||||||
)
|
|
||||||
{
|
|
||||||
std::istringstream requestLine(line);
|
|
||||||
std::string method, path, version;
|
|
||||||
requestLine >> method >> path >> version;
|
|
||||||
|
|
||||||
if (method.empty() || path.empty() || version.empty()) {
|
|
||||||
throw std::runtime_error("Invalid HTTP request line");
|
|
||||||
}
|
|
||||||
|
|
||||||
req.setMethod(method);
|
|
||||||
req.setPath(path);
|
|
||||||
req.setVersion(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse headers until empty line
|
|
||||||
void HttpRequestParser::parseHeaders(
|
|
||||||
std::istringstream &stream, HttpRequest &req
|
|
||||||
)
|
|
||||||
{
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(stream, line)) {
|
|
||||||
if (!line.empty() && line.back() == '\r') {
|
|
||||||
line.pop_back();
|
|
||||||
}
|
|
||||||
if (line.empty()) {
|
|
||||||
break; // end of headers
|
|
||||||
}
|
|
||||||
parseHeaderLine(line, req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse single "Key: Value" header
|
|
||||||
void HttpRequestParser::parseHeaderLine(
|
|
||||||
const std::string &line, HttpRequest &req
|
|
||||||
)
|
|
||||||
{
|
|
||||||
auto colon = line.find(':');
|
|
||||||
if (colon == std::string::npos) {
|
|
||||||
return; // skip invalid header
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string key = line.substr(0, colon);
|
|
||||||
std::string value = line.substr(colon + 1);
|
|
||||||
|
|
||||||
boost::algorithm::trim(key);
|
|
||||||
boost::algorithm::trim(value);
|
|
||||||
|
|
||||||
req.addHeader(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse optional body after headers
|
|
||||||
void HttpRequestParser::parseBody(std::istringstream &stream, HttpRequest &req)
|
|
||||||
{
|
|
||||||
std::string body;
|
|
||||||
std::getline(stream, body, '\0');
|
|
||||||
if (!body.empty()) {
|
|
||||||
req.setBody(body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "http_request.hpp"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class HttpRequestParser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static HttpRequest parse(const std::string &raw);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void parseRequestLine(const std::string &line, HttpRequest &req);
|
|
||||||
static void parseHeaderLine(const std::string &line, HttpRequest &req);
|
|
||||||
static void parseHeaders(std::istringstream &stream, HttpRequest &req);
|
|
||||||
static void parseBody(std::istringstream &stream, HttpRequest &req);
|
|
||||||
};
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
#include "http_response.hpp"
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
HttpResponse::HttpResponse()
|
|
||||||
: statusCode_(200)
|
|
||||||
, reasonPhrase_("OK")
|
|
||||||
, version_("HTTP/1.1")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpResponse::setStatusCode(int code)
|
|
||||||
{
|
|
||||||
statusCode_ = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
int HttpResponse::getStatusCode() const
|
|
||||||
{
|
|
||||||
return statusCode_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpResponse::setReasonPhrase(const std::string_view phrase)
|
|
||||||
{
|
|
||||||
reasonPhrase_ = phrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string_view HttpResponse::getReasonPhrase() const
|
|
||||||
{
|
|
||||||
return reasonPhrase_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpResponse::setVersion(const std::string_view version)
|
|
||||||
{
|
|
||||||
version_ = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string_view HttpResponse::getVersion() const
|
|
||||||
{
|
|
||||||
return version_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpResponse::addHeader(
|
|
||||||
const std::string_view key, const std::string_view value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
headers_[std::string(key)] = std::string(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HttpResponse::hasHeader(const std::string_view key) const
|
|
||||||
{
|
|
||||||
return headers_.find(std::string(key)) != headers_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HttpResponse::getHeader(const std::string_view key) const
|
|
||||||
{
|
|
||||||
auto it = headers_.find(std::string(key));
|
|
||||||
if (it == headers_.end()) {
|
|
||||||
throw std::runtime_error("Header not found: " + std::string(key));
|
|
||||||
}
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::unordered_map<std::string, std::string> &
|
|
||||||
HttpResponse::getHeaders() const
|
|
||||||
{
|
|
||||||
return headers_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpResponse::setBody(const std::string_view body)
|
|
||||||
{
|
|
||||||
body_ = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string_view HttpResponse::getBody() const
|
|
||||||
{
|
|
||||||
return body_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HttpResponse::toString() const
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
|
|
||||||
oss << version_ << " " << statusCode_ << " " << reasonPhrase_ << "\r\n";
|
|
||||||
|
|
||||||
for (const auto &[key, value] : headers_) {
|
|
||||||
oss << key << ": " << value << "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
oss << "\r\n";
|
|
||||||
|
|
||||||
oss << body_;
|
|
||||||
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
class HttpResponse
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HttpResponse();
|
|
||||||
|
|
||||||
void setStatusCode(int code);
|
|
||||||
int getStatusCode() const;
|
|
||||||
|
|
||||||
void setReasonPhrase(const std::string_view phrase);
|
|
||||||
const std::string_view getReasonPhrase() const;
|
|
||||||
|
|
||||||
void setVersion(const std::string_view version);
|
|
||||||
const std::string_view getVersion() const;
|
|
||||||
|
|
||||||
void addHeader(const std::string_view key, const std::string_view value);
|
|
||||||
bool hasHeader(const std::string_view key) const;
|
|
||||||
std::string getHeader(const std::string_view key) const;
|
|
||||||
const std::unordered_map<std::string, std::string> &getHeaders() const;
|
|
||||||
|
|
||||||
void setBody(const std::string_view body);
|
|
||||||
const std::string_view getBody() const;
|
|
||||||
|
|
||||||
std::string toString() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int statusCode_;
|
|
||||||
std::string reasonPhrase_;
|
|
||||||
std::string version_;
|
|
||||||
std::unordered_map<std::string, std::string> headers_;
|
|
||||||
std::string body_;
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#include "http_server.hpp"
|
|
||||||
|
|
||||||
#include "http_server.hpp"
|
|
||||||
#include "http_session.hpp"
|
|
||||||
|
|
||||||
HttpServer::HttpServer(IoContext &io, unsigned short port, std::string rootDir)
|
|
||||||
: TcpServerBase(io, port)
|
|
||||||
, rootDir_(std::move(rootDir))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpServer::createSession(TcpSocket socket)
|
|
||||||
{
|
|
||||||
std::make_shared<HttpSession>(std::move(socket), rootDir_)->start();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "tcp_server_base.hpp"
|
|
||||||
|
|
||||||
class HttpServer : public TcpServerBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HttpServer(IoContext &io, unsigned short port, std::string rootDir);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void createSession(TcpSocket socket) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string rootDir_;
|
|
||||||
};
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#include "http_session.hpp"
|
|
||||||
|
|
||||||
#include "session_base.hpp"
|
|
||||||
#include "http_request.hpp"
|
|
||||||
#include "http_request_parser.hpp"
|
|
||||||
#include "http_response.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
HttpSession::HttpSession(TcpSocket socket, std::string rootDir)
|
|
||||||
: SessionBase(std::move(socket))
|
|
||||||
, rootDir_(std::move(rootDir))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpSession::handleRequest(const std::string_view rawRequest)
|
|
||||||
{
|
|
||||||
HttpRequest req = HttpRequestParser::parse(std::string(rawRequest));
|
|
||||||
|
|
||||||
HttpResponse res;
|
|
||||||
|
|
||||||
if (req.getPath().rfind("/file/", 0) == 0) {
|
|
||||||
// remove "/file/" prefix
|
|
||||||
std::string filePath = rootDir_ + "/" + req.getPath().substr(6);
|
|
||||||
|
|
||||||
std::ifstream file(filePath, std::ios::binary);
|
|
||||||
if (file) {
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << file.rdbuf();
|
|
||||||
|
|
||||||
std::string content = ss.str();
|
|
||||||
res.setStatusCode(200);
|
|
||||||
res.setReasonPhrase("OK");
|
|
||||||
res.setBody(content);
|
|
||||||
res.addHeader("Content-Type", "text/plain");
|
|
||||||
res.addHeader("Content-Length", std::to_string(content.size()));
|
|
||||||
} else {
|
|
||||||
res.setStatusCode(404);
|
|
||||||
res.setReasonPhrase("Not Found");
|
|
||||||
res.setBody("File not found");
|
|
||||||
res.addHeader("Content-Type", "text/plain");
|
|
||||||
res.addHeader(
|
|
||||||
"Content-Length", std::to_string(res.getBody().size())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.setStatusCode(200);
|
|
||||||
res.setReasonPhrase("OK");
|
|
||||||
res.setBody("Hello, World!");
|
|
||||||
res.addHeader("Content-Type", "text/plain");
|
|
||||||
res.addHeader("Content-Length", std::to_string(res.getBody().size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string responseStr = res.toString();
|
|
||||||
doWrite(responseStr);
|
|
||||||
|
|
||||||
Logger::info(
|
|
||||||
req.getMethod() + " " + req.getPath() + " -> "
|
|
||||||
+ std::to_string(res.getStatusCode())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "session_base.hpp"
|
|
||||||
|
|
||||||
class HttpSession : public SessionBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HttpSession(TcpSocket socket, std::string rootDir);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void handleRequest(const std::string_view rawRequest) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string rootDir_;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#include "cli_base.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "port_scanner.hpp"
|
||||||
|
#include "portscan_cli.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static std::vector<uint16_t> allPorts()
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> v;
|
||||||
|
v.reserve(65535);
|
||||||
|
for (uint32_t p = 1; p <= 65535; ++p) {
|
||||||
|
v.push_back(static_cast<uint16_t>(p));
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Logger::init(true);
|
||||||
|
|
||||||
|
PortScanCli cli;
|
||||||
|
cli.parse(argc, argv);
|
||||||
|
if (cli.isHelp()) {
|
||||||
|
cli.printHelp();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ip = cli.getIp();
|
||||||
|
auto ports = allPorts();
|
||||||
|
|
||||||
|
Logger::info("Starting full port scan (1..65535)...");
|
||||||
|
PortScanner scanner(ip, ports);
|
||||||
|
scanner.run();
|
||||||
|
Logger::info("Scan completed.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#include "dns_cli.hpp"
|
|
||||||
#include "dns_server.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
DnsCli cli;
|
|
||||||
cli.parse(argc, argv);
|
|
||||||
|
|
||||||
if (cli.isHelp()) {
|
|
||||||
cli.printHelp();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::init(true, "dns_server.log");
|
|
||||||
Logger::info(
|
|
||||||
"DNS server starting on port " + std::to_string(cli.getPort())
|
|
||||||
+ ", answering with IP " + cli.getIp()
|
|
||||||
);
|
|
||||||
|
|
||||||
IoContext io;
|
|
||||||
DnsServer server(io, cli.getPort(), cli.getIp());
|
|
||||||
server.start();
|
|
||||||
|
|
||||||
io.run();
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#include "file_cli.hpp"
|
|
||||||
#include "file_server.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
FileCli cli;
|
|
||||||
cli.parse(argc, argv);
|
|
||||||
|
|
||||||
if (cli.isHelp()) {
|
|
||||||
cli.printHelp();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::init(true, "file_server.log");
|
|
||||||
|
|
||||||
IoContext io;
|
|
||||||
FileServer server(io, cli.getPort(), cli.getRootDir());
|
|
||||||
|
|
||||||
Logger::info(
|
|
||||||
"File server starting on port " + std::to_string(cli.getPort())
|
|
||||||
+ ", root dir: " + cli.getRootDir()
|
|
||||||
);
|
|
||||||
|
|
||||||
server.start();
|
|
||||||
io.run();
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#include "http_cli.hpp"
|
|
||||||
#include "http_server.hpp"
|
|
||||||
#include "logger.hpp"
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
HttpCli cli;
|
|
||||||
cli.parse(argc, argv);
|
|
||||||
|
|
||||||
if (cli.isHelp()) {
|
|
||||||
cli.printHelp();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::init(true, "http_server.log");
|
|
||||||
|
|
||||||
IoContext io;
|
|
||||||
HttpServer server(io, cli.getPort(), cli.getRootDir());
|
|
||||||
|
|
||||||
Logger::info(
|
|
||||||
"Http server starting on port " + std::to_string(cli.getPort())
|
|
||||||
+ ", root dir: " + cli.getRootDir()
|
|
||||||
);
|
|
||||||
|
|
||||||
server.start();
|
|
||||||
io.run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Test DNS server on port 5300
|
|
||||||
dig @127.0.0.1 -p 5300 example.com
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Test File server on port 9090
|
|
||||||
# Send file name "test.txt" to server and print response
|
|
||||||
echo "test.txt" | nc 127.0.0.1 9090
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Test HTTP server on port 8080
|
|
||||||
curl -v http://127.0.0.1:8080/
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Hello from file!
|
|
||||||
@@ -10,29 +10,10 @@ add_requires("cmake::Boost", {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
-- HTTP server target
|
target "port_scanner"
|
||||||
target "http_server"
|
|
||||||
do
|
do
|
||||||
set_kind "binary"
|
set_kind "binary"
|
||||||
add_files("src/main/main_http.cpp", "src/http/*.cpp", "src/common/*.cpp")
|
add_files("src/main.cpp", "src/cli/*.cpp", "src/common/*.cpp", "src/core/*.cpp")
|
||||||
add_includedirs("src", "src/http", "src/common", { public = true })
|
add_includedirs("src", "src/cli", "src/common", "src/core", { public = true })
|
||||||
add_packages "boost"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- File server target
|
|
||||||
target "file_server"
|
|
||||||
do
|
|
||||||
set_kind "binary"
|
|
||||||
add_files("src/main/main_file.cpp", "src/file/*.cpp", "src/common/*.cpp")
|
|
||||||
add_includedirs("src", "src/file", "src/common", { public = true })
|
|
||||||
add_packages "boost"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- DNS server target
|
|
||||||
target "dns_server"
|
|
||||||
do
|
|
||||||
set_kind "binary"
|
|
||||||
add_files("src/main/main_dns.cpp", "src/dns/*.cpp", "src/common/*.cpp")
|
|
||||||
add_includedirs("src", "src/dns", "src/common", { public = true })
|
|
||||||
add_packages "boost"
|
add_packages "boost"
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user