mirror of
https://github.com/rjhansen/nsrlsvr.git
synced 2026-01-09 14:28:05 -05:00
Ran through clang-format.
This commit is contained in:
232
src/handler.cc
232
src/handler.cc
@@ -14,151 +14,145 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "main.h"
|
||||
|
||||
using std::string;
|
||||
using std::transform;
|
||||
using std::vector;
|
||||
using std::exception;
|
||||
using std::binary_search;
|
||||
using std::pair;
|
||||
using std::back_inserter;
|
||||
using std::getline;
|
||||
using std::stringstream;
|
||||
using std::to_string;
|
||||
using boost::asio::ip::tcp;
|
||||
using boost::char_separator;
|
||||
using boost::tokenizer;
|
||||
using boost::asio::ip::tcp;
|
||||
using std::back_inserter;
|
||||
using std::binary_search;
|
||||
using std::exception;
|
||||
using std::getline;
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::to_string;
|
||||
using std::transform;
|
||||
using std::vector;
|
||||
|
||||
// defined in main.cc
|
||||
extern const vector<pair64>& hashes;
|
||||
|
||||
namespace {
|
||||
enum class Command {
|
||||
Version = 0,
|
||||
Bye = 1,
|
||||
Status = 2,
|
||||
Query = 3,
|
||||
Upshift = 4,
|
||||
Downshift = 5,
|
||||
Unknown = 6
|
||||
Version = 0,
|
||||
Bye = 1,
|
||||
Status = 2,
|
||||
Query = 3,
|
||||
Upshift = 4,
|
||||
Downshift = 5,
|
||||
Unknown = 6
|
||||
};
|
||||
|
||||
auto tokenize(const string& line)
|
||||
{
|
||||
vector<string> rv;
|
||||
char_separator<char> sep(" ");
|
||||
tokenizer<char_separator<char>> tokens(line, sep);
|
||||
for (const auto& t : tokens) {
|
||||
rv.emplace_back(t);
|
||||
}
|
||||
return rv;
|
||||
auto tokenize(const string& line) {
|
||||
vector<string> rv;
|
||||
char_separator<char> sep(" ");
|
||||
tokenizer<char_separator<char>> tokens(line, sep);
|
||||
for (const auto& t : tokens) {
|
||||
rv.emplace_back(t);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool is_present_in_hashes(const string& hash)
|
||||
{
|
||||
return binary_search(hashes.cbegin(), hashes.cend(), to_pair64(hash));
|
||||
bool is_present_in_hashes(const string& hash) {
|
||||
return binary_search(hashes.cbegin(), hashes.cend(), to_pair64(hash));
|
||||
}
|
||||
|
||||
auto getCommand(const string& cmdstring)
|
||||
{
|
||||
string localcmd = "";
|
||||
transform(cmdstring.cbegin(), cmdstring.cend(), back_inserter(localcmd), ::toupper);
|
||||
auto getCommand(const string& cmdstring) {
|
||||
string localcmd = "";
|
||||
transform(cmdstring.cbegin(), cmdstring.cend(), back_inserter(localcmd),
|
||||
::toupper);
|
||||
|
||||
auto cmd = Command::Unknown;
|
||||
auto cmd = Command::Unknown;
|
||||
|
||||
if (localcmd == "VERSION:")
|
||||
cmd = Command::Version;
|
||||
else if (localcmd == "BYE")
|
||||
cmd = Command::Bye;
|
||||
else if (localcmd == "STATUS")
|
||||
cmd = Command::Status;
|
||||
else if (localcmd == "QUERY")
|
||||
cmd = Command::Query;
|
||||
else if (localcmd == "UPSHIFT")
|
||||
cmd = Command::Upshift;
|
||||
else if (localcmd == "DOWNSHIFT")
|
||||
cmd = Command::Downshift;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
if (localcmd == "VERSION:")
|
||||
cmd = Command::Version;
|
||||
else if (localcmd == "BYE")
|
||||
cmd = Command::Bye;
|
||||
else if (localcmd == "STATUS")
|
||||
cmd = Command::Status;
|
||||
else if (localcmd == "QUERY")
|
||||
cmd = Command::Query;
|
||||
else if (localcmd == "UPSHIFT")
|
||||
cmd = Command::Upshift;
|
||||
else if (localcmd == "DOWNSHIFT")
|
||||
cmd = Command::Downshift;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void handle_client(tcp::iostream& stream)
|
||||
{
|
||||
const string ipaddr = stream.socket().remote_endpoint().address().to_string();
|
||||
unsigned long long queries = 0;
|
||||
try {
|
||||
while (stream) {
|
||||
string line;
|
||||
getline(stream, line);
|
||||
if (line.size() == 0) return;
|
||||
void handle_client(tcp::iostream& stream) {
|
||||
const string ipaddr = stream.socket().remote_endpoint().address().to_string();
|
||||
unsigned long long queries = 0;
|
||||
try {
|
||||
while (stream) {
|
||||
string line;
|
||||
getline(stream, line);
|
||||
if (line.size() == 0) return;
|
||||
|
||||
// trim leading/following whitespace
|
||||
auto end_ws = line.find_last_not_of("\t\n\v\f\r ");
|
||||
if (end_ws != string::npos) {
|
||||
line.erase(end_ws + 1);
|
||||
}
|
||||
auto front_ws = line.find_first_not_of("\t\n\v\f\r ");
|
||||
if (front_ws > 0) {
|
||||
line.erase(0, front_ws);
|
||||
}
|
||||
// trim leading/following whitespace
|
||||
auto end_ws = line.find_last_not_of("\t\n\v\f\r ");
|
||||
if (end_ws != string::npos) {
|
||||
line.erase(end_ws + 1);
|
||||
}
|
||||
auto front_ws = line.find_first_not_of("\t\n\v\f\r ");
|
||||
if (front_ws > 0) {
|
||||
line.erase(0, front_ws);
|
||||
}
|
||||
|
||||
auto commands = tokenize(line);
|
||||
switch (getCommand(commands.at(0))) {
|
||||
case Command::Version:
|
||||
stream << "OK\r\n";
|
||||
break;
|
||||
auto commands = tokenize(line);
|
||||
switch (getCommand(commands.at(0))) {
|
||||
case Command::Version:
|
||||
stream << "OK\r\n";
|
||||
break;
|
||||
|
||||
case Command::Bye:
|
||||
return;
|
||||
case Command::Bye:
|
||||
return;
|
||||
|
||||
case Command::Status:
|
||||
stream << "NOT SUPPORTED\r\n";
|
||||
break;
|
||||
case Command::Status:
|
||||
stream << "NOT SUPPORTED\r\n";
|
||||
break;
|
||||
|
||||
case Command::Query:
|
||||
{
|
||||
stringstream rv;
|
||||
rv << "OK ";
|
||||
for (size_t idx = 1 ; idx < commands.size(); ++idx)
|
||||
rv << (is_present_in_hashes(commands.at(idx)) ? "1" : "0");
|
||||
rv << "\r\n";
|
||||
queries += (commands.size() - 1);
|
||||
stream << rv.str();
|
||||
break;
|
||||
}
|
||||
|
||||
case Command::Upshift:
|
||||
stream << "NOT OK\r\n";
|
||||
break;
|
||||
|
||||
case Command::Downshift:
|
||||
stream << "NOT OK\r\n";
|
||||
break;
|
||||
|
||||
case Command::Unknown:
|
||||
stream << "NOT OK\r\n";
|
||||
return;
|
||||
}
|
||||
case Command::Query: {
|
||||
stringstream rv;
|
||||
rv << "OK ";
|
||||
for (size_t idx = 1; idx < commands.size(); ++idx)
|
||||
rv << (is_present_in_hashes(commands.at(idx)) ? "1" : "0");
|
||||
rv << "\r\n";
|
||||
queries += (commands.size() - 1);
|
||||
stream << rv.str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
log(LogLevel::ALERT, string("Error: ") + e.what());
|
||||
// swallow the exception: we'll close the connection
|
||||
// automagically on exit
|
||||
//
|
||||
// fall-through here to function returb
|
||||
}
|
||||
|
||||
stringstream status_msg;
|
||||
status_msg << ipaddr << " closed session after " << queries
|
||||
<< " queries";
|
||||
log(LogLevel::ALERT, status_msg.str());
|
||||
case Command::Upshift:
|
||||
stream << "NOT OK\r\n";
|
||||
break;
|
||||
|
||||
case Command::Downshift:
|
||||
stream << "NOT OK\r\n";
|
||||
break;
|
||||
|
||||
case Command::Unknown:
|
||||
stream << "NOT OK\r\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
log(LogLevel::ALERT, string("Error: ") + e.what());
|
||||
// swallow the exception: we'll close the connection
|
||||
// automagically on exit
|
||||
//
|
||||
// fall-through here to function returb
|
||||
}
|
||||
|
||||
stringstream status_msg;
|
||||
status_msg << ipaddr << " closed session after " << queries << " queries";
|
||||
log(LogLevel::ALERT, status_msg.str());
|
||||
}
|
||||
|
||||
508
src/main.cc
508
src/main.cc
@@ -15,277 +15,272 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <algorithm>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits.h>
|
||||
#include <regex>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
using std::string;
|
||||
using std::transform;
|
||||
using std::ifstream;
|
||||
using boost::asio::ip::tcp;
|
||||
using boost::program_options::notify;
|
||||
using boost::program_options::options_description;
|
||||
using boost::program_options::parse_command_line;
|
||||
using boost::program_options::store;
|
||||
using boost::program_options::value;
|
||||
using boost::program_options::variables_map;
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::vector;
|
||||
using std::sort;
|
||||
using std::fill;
|
||||
using std::getline;
|
||||
using std::ifstream;
|
||||
using std::pair;
|
||||
using std::regex;
|
||||
using std::sort;
|
||||
using std::stoi;
|
||||
using std::string;
|
||||
using std::to_string;
|
||||
using std::getline;
|
||||
using std::fill;
|
||||
using boost::program_options::options_description;
|
||||
using boost::program_options::variables_map;
|
||||
using boost::program_options::store;
|
||||
using boost::program_options::parse_command_line;
|
||||
using boost::program_options::notify;
|
||||
using boost::program_options::value;
|
||||
using boost::asio::ip::tcp;
|
||||
using std::transform;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
vector<pair64> hash_set;
|
||||
string hashes_location{ PKGDATADIR "/hashes.txt" };
|
||||
uint16_t port{ 9120 };
|
||||
bool dry_run{ false };
|
||||
string hashes_location{PKGDATADIR "/hashes.txt"};
|
||||
uint16_t port{9120};
|
||||
bool dry_run{false};
|
||||
|
||||
/** Attempts to load a set of MD5 hashes from disk.
|
||||
* Each line must be either blank or 32 hexadecimal digits. If the
|
||||
* file doesn't conform to this, nsrlsvr will abort and display an
|
||||
* error message to the log.
|
||||
*/
|
||||
void load_hashes()
|
||||
{
|
||||
const regex md5_re{ "^[A-Fa-f0-9]{32}$" };
|
||||
uint32_t hash_count{ 0 };
|
||||
ifstream infile{ hashes_location.c_str() };
|
||||
* Each line must be either blank or 32 hexadecimal digits. If the
|
||||
* file doesn't conform to this, nsrlsvr will abort and display an
|
||||
* error message to the log.
|
||||
*/
|
||||
void load_hashes() {
|
||||
const regex md5_re{"^[A-Fa-f0-9]{32}$"};
|
||||
uint32_t hash_count{0};
|
||||
ifstream infile{hashes_location.c_str()};
|
||||
|
||||
// As of this writing, the full RDS had about 45 million entries.
|
||||
// When a vector needs to grow, it normally does so by doubling
|
||||
// the former allocation -- so after this, the next stop is a
|
||||
// 100 million allocation (@ 16 bytes per, or 1.6 GB). If you're
|
||||
// maintaining this code, try to keep the reserve a few million
|
||||
// larger than the RDS currently is, to give yourself room to
|
||||
// grow without a vector realloc.
|
||||
//
|
||||
// Failure to reserve this block of memory is non-recoverable.
|
||||
// Don't even try. Just log the error and bail out. Let the end
|
||||
// user worry about installing more RAM.
|
||||
try {
|
||||
hash_set.reserve(50000000);
|
||||
} catch (std::bad_alloc&) {
|
||||
log(LogLevel::ALERT, "couldn't reserve enough memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (not infile) {
|
||||
log(LogLevel::ALERT, "couldn't open hashes file " + hashes_location);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (infile) {
|
||||
string line;
|
||||
getline(infile, line);
|
||||
transform(line.begin(), line.end(), line.begin(), ::toupper);
|
||||
if (0 == line.size()) continue;
|
||||
|
||||
if (!regex_match(line.cbegin(), line.cend(), md5_re)) {
|
||||
log(LogLevel::ALERT, "hash file appears corrupt! Loading no hashes.");
|
||||
log(LogLevel::ALERT, "offending line is: " + line);
|
||||
log(LogLevel::ALERT, "shutting down!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// As of this writing, the full RDS had about 65 million entries.
|
||||
// When a vector needs to grow, it normally does so by doubling
|
||||
// the former allocation -- so after this, the next stop is a
|
||||
// 100 million allocation (@ 16 bytes per, or 1.6 GB). If you're
|
||||
// maintaining this code, try to keep the reserve a few million
|
||||
// larger than the RDS currently is, to give yourself room to
|
||||
// grow without a vector realloc.
|
||||
//
|
||||
// Failure to reserve this block of memory is non-recoverable.
|
||||
// Don't even try. Just log the error and bail out. Let the end
|
||||
// user worry about installing more RAM.
|
||||
try {
|
||||
hash_set.reserve(50000000);
|
||||
// .emplace_back is the C++11 improvement over the old
|
||||
// vector.push_back. It has the benefit of not needing
|
||||
// to construct a temporary to hold the value; it can
|
||||
// just construct-in-place. For 40 million values, that
|
||||
// can be significant.
|
||||
//
|
||||
// Note that if the vector runs out of reserved room it
|
||||
// will attempt to make a new allocation double the size
|
||||
// of the last. That means the application will at least
|
||||
// briefly need *three times* the expected RAM -- one for
|
||||
// the data set and two for the newly-allocated chunk.
|
||||
// Given we're talking about multiple gigs of RAM, this
|
||||
// .emplace_back needs to consider the possibility of a
|
||||
// RAM allocation failure.
|
||||
hash_set.emplace_back(to_pair64(line));
|
||||
hash_count += 1;
|
||||
if (0 == hash_count % 1000000) {
|
||||
string howmany{to_string(hash_count / 1000000)};
|
||||
log(LogLevel::INFO, "loaded " + howmany + " million hashes");
|
||||
}
|
||||
} catch (std::bad_alloc&) {
|
||||
log(LogLevel::ALERT, "couldn't reserve enough memory");
|
||||
log(LogLevel::ALERT, "couldn't allocate enough memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
string howmany{to_string(hash_count)};
|
||||
log(LogLevel::INFO, "read in " + howmany + " hashes");
|
||||
|
||||
infile.close();
|
||||
|
||||
sort(hash_set.begin(), hash_set.end());
|
||||
|
||||
if (hash_set.size() > 1) {
|
||||
log(LogLevel::INFO, "ensuring no duplicates");
|
||||
for (auto iter = (hash_set.cbegin() + 1); iter != hash_set.cend(); ++iter) {
|
||||
if (*(iter - 1) == *iter) {
|
||||
log(LogLevel::ALERT,
|
||||
"hash file contains duplicates -- "
|
||||
"shutting down!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (not infile) {
|
||||
log(LogLevel::ALERT, "couldn't open hashes file " + hashes_location);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (infile) {
|
||||
string line;
|
||||
getline(infile, line);
|
||||
transform(line.begin(), line.end(), line.begin(), ::toupper);
|
||||
if (0 == line.size())
|
||||
continue;
|
||||
|
||||
if (!regex_match(line.cbegin(), line.cend(), md5_re)) {
|
||||
log(LogLevel::ALERT, "hash file appears corrupt! Loading no hashes.");
|
||||
log(LogLevel::ALERT, "offending line is: " + line);
|
||||
log(LogLevel::ALERT, "shutting down!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
try {
|
||||
// .emplace_back is the C++11 improvement over the old
|
||||
// vector.push_back. It has the benefit of not needing
|
||||
// to construct a temporary to hold the value; it can
|
||||
// just construct-in-place. For 40 million values, that
|
||||
// can be significant.
|
||||
//
|
||||
// Note that if the vector runs out of reserved room it
|
||||
// will attempt to make a new allocation double the size
|
||||
// of the last. That means the application will at least
|
||||
// briefly need *three times* the expected RAM -- one for
|
||||
// the data set and two for the newly-allocated chunk.
|
||||
// Given we're talking about multiple gigs of RAM, this
|
||||
// .emplace_back needs to consider the possibility of a
|
||||
// RAM allocation failure.
|
||||
hash_set.emplace_back(to_pair64(line));
|
||||
hash_count += 1;
|
||||
if (0 == hash_count % 1000000) {
|
||||
string howmany{ to_string(hash_count / 1000000) };
|
||||
log(LogLevel::INFO, "loaded " + howmany + " million hashes");
|
||||
}
|
||||
} catch (std::bad_alloc&) {
|
||||
log(LogLevel::ALERT, "couldn't allocate enough memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
string howmany{ to_string(hash_count) };
|
||||
log(LogLevel::INFO, "read in " + howmany + " hashes");
|
||||
|
||||
infile.close();
|
||||
|
||||
sort(hash_set.begin(), hash_set.end());
|
||||
|
||||
if (hash_set.size() > 1) {
|
||||
log(LogLevel::INFO, "ensuring no duplicates");
|
||||
for (auto iter = (hash_set.cbegin() + 1); iter != hash_set.cend(); ++iter) {
|
||||
if (*(iter - 1) == *iter) {
|
||||
log(LogLevel::ALERT, "hash file contains duplicates -- "
|
||||
"shutting down!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log(LogLevel::INFO, "successfully loaded hashes");
|
||||
log(LogLevel::INFO, "successfully loaded hashes");
|
||||
}
|
||||
|
||||
/** Converts this process into a well-behaved UNIX daemon.*/
|
||||
void daemonize()
|
||||
{
|
||||
/* Nothing in here should be surprising. If it is, then please
|
||||
check the standard literature to ensure you understand how a
|
||||
daemon is supposed to work. */
|
||||
const auto pid = fork();
|
||||
if (0 > pid) {
|
||||
log(LogLevel::WARN, "couldn't fork!");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (0 < pid) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
log(LogLevel::INFO, "daemon started");
|
||||
void daemonize() {
|
||||
/* Nothing in here should be surprising. If it is, then please
|
||||
check the standard literature to ensure you understand how a
|
||||
daemon is supposed to work. */
|
||||
const auto pid = fork();
|
||||
if (0 > pid) {
|
||||
log(LogLevel::WARN, "couldn't fork!");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (0 < pid) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
log(LogLevel::INFO, "daemon started");
|
||||
|
||||
umask(0);
|
||||
umask(0);
|
||||
|
||||
if (0 > setsid()) {
|
||||
log(LogLevel::WARN, "couldn't set sid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (0 > setsid()) {
|
||||
log(LogLevel::WARN, "couldn't set sid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (0 > chdir("/")) {
|
||||
log(LogLevel::WARN, "couldn't chdir to root");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (0 > chdir("/")) {
|
||||
log(LogLevel::WARN, "couldn't chdir to root");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
}
|
||||
|
||||
/** Parse command-line options.
|
||||
@param argc argc from main()
|
||||
@param argv argv from main()
|
||||
*/
|
||||
void parse_options(int argc, char* argv[])
|
||||
{
|
||||
std::array<char, PATH_MAX> filename_buffer;
|
||||
char* filepath{ &filename_buffer[0] };
|
||||
fill(filename_buffer.begin(), filename_buffer.end(), 0);
|
||||
options_description options{ "nsrlsvr options" };
|
||||
options.add_options()("help,h", "Help screen")("version,v",
|
||||
"Display package version")(
|
||||
"bug-report,b", "Display bug reporting information")(
|
||||
"file,f", value<string>()->default_value(PKGDATADIR "/hashes.txt"),
|
||||
"hash file")(
|
||||
"port,p", value<uint16_t>()->default_value(9120), "port")(
|
||||
"dry-run", "test configuration");
|
||||
variables_map vm;
|
||||
store(parse_command_line(argc, argv, options), vm);
|
||||
void parse_options(int argc, char* argv[]) {
|
||||
std::array<char, PATH_MAX> filename_buffer;
|
||||
char* filepath{&filename_buffer[0]};
|
||||
fill(filename_buffer.begin(), filename_buffer.end(), 0);
|
||||
options_description options{"nsrlsvr options"};
|
||||
options.add_options()("help,h", "Help screen")("version,v",
|
||||
"Display package version")(
|
||||
"bug-report,b", "Display bug reporting information")(
|
||||
"file,f", value<string>()->default_value(PKGDATADIR "/hashes.txt"),
|
||||
"hash file")("port,p", value<uint16_t>()->default_value(9120), "port")(
|
||||
"dry-run", "test configuration");
|
||||
variables_map vm;
|
||||
store(parse_command_line(argc, argv, options), vm);
|
||||
|
||||
dry_run = vm.count("dry-run") ? true : false;
|
||||
dry_run = vm.count("dry-run") ? true : false;
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << options << "\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if (vm.count("version")) {
|
||||
cout << "nsrlsvr version " << PACKAGE_VERSION
|
||||
<< "\n\n"
|
||||
"This program is released under the ISC License.\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if (vm.count("bug-report")) {
|
||||
cout << "To file a bug report, visit "
|
||||
"https://github.com/rjhansen/nsrlsvr/issues\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
port = vm["port"].as<uint16_t>();
|
||||
string relpath = vm["file"].as<string>();
|
||||
if (nullptr == (filepath = realpath(relpath.c_str(), filepath))) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
cerr << "Could not access file path " << relpath
|
||||
<< "\n(Do you have read privileges?)\n";
|
||||
break;
|
||||
case EINVAL:
|
||||
cerr << "Somehow, the system believes the file path passed to it\n"
|
||||
"is null. This is weird and probably a bug. Please report\n"
|
||||
"it!\n";
|
||||
break;
|
||||
case EIO:
|
||||
cerr << "An I/O error occurred while reading " << relpath << "\n";
|
||||
break;
|
||||
case ELOOP:
|
||||
cerr << "Too many symbolic links were found while translating "
|
||||
<< relpath << " into an absolute path.\n";
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
cerr << "The file path " << relpath << " is too long.\n";
|
||||
break;
|
||||
case ENOENT:
|
||||
cerr << "The file " << relpath << " could not be found.\n";
|
||||
break;
|
||||
case ENOMEM:
|
||||
cerr << "Strangely, the system ran out of memory while processing\n"
|
||||
"your request. This is probably a bug in nsrlsvr.\n";
|
||||
break;
|
||||
case ENOTDIR:
|
||||
cerr << "A component of the file path " << relpath
|
||||
<< " is not a directory.";
|
||||
break;
|
||||
default:
|
||||
cerr << "... wtfbbq? This should never trip. It's an nsrlsvr bug.\n";
|
||||
break;
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
hashes_location = string(filepath);
|
||||
if (not ifstream(hashes_location.c_str())) {
|
||||
cerr << "Could not open " + hashes_location + " for reading.\n";
|
||||
exit(EXIT_FAILURE);
|
||||
if (vm.count("help")) {
|
||||
cout << options << "\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if (vm.count("version")) {
|
||||
cout << "nsrlsvr version " << PACKAGE_VERSION
|
||||
<< "\n\n"
|
||||
"This program is released under the ISC License.\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if (vm.count("bug-report")) {
|
||||
cout << "To file a bug report, visit "
|
||||
"https://github.com/rjhansen/nsrlsvr/issues\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
port = vm["port"].as<uint16_t>();
|
||||
string relpath = vm["file"].as<string>();
|
||||
if (nullptr == (filepath = realpath(relpath.c_str(), filepath))) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
cerr << "Could not access file path " << relpath
|
||||
<< "\n(Do you have read privileges?)\n";
|
||||
break;
|
||||
case EINVAL:
|
||||
cerr << "Somehow, the system believes the file path passed to it\n"
|
||||
"is null. This is weird and probably a bug. Please report\n"
|
||||
"it!\n";
|
||||
break;
|
||||
case EIO:
|
||||
cerr << "An I/O error occurred while reading " << relpath << "\n";
|
||||
break;
|
||||
case ELOOP:
|
||||
cerr << "Too many symbolic links were found while translating "
|
||||
<< relpath << " into an absolute path.\n";
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
cerr << "The file path " << relpath << " is too long.\n";
|
||||
break;
|
||||
case ENOENT:
|
||||
cerr << "The file " << relpath << " could not be found.\n";
|
||||
break;
|
||||
case ENOMEM:
|
||||
cerr << "Strangely, the system ran out of memory while processing\n"
|
||||
"your request. This is probably a bug in nsrlsvr.\n";
|
||||
break;
|
||||
case ENOTDIR:
|
||||
cerr << "A component of the file path " << relpath
|
||||
<< " is not a directory.";
|
||||
break;
|
||||
default:
|
||||
cerr << "... wtfbbq? This should never trip. It's an nsrlsvr bug.\n";
|
||||
break;
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
hashes_location = string(filepath);
|
||||
if (not ifstream(hashes_location.c_str())) {
|
||||
cerr << "Could not open " + hashes_location + " for reading.\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/** The set of all loaded hashes, represented as a const reference. */
|
||||
const vector<pair64>& hashes{ hash_set };
|
||||
const vector<pair64>& hashes{hash_set};
|
||||
|
||||
/** Writes to syslog with the given priority level.
|
||||
|
||||
@param level The priority of the message
|
||||
@param msg The message to write
|
||||
*/
|
||||
void log(const LogLevel level, const string&& msg)
|
||||
{
|
||||
if (dry_run)
|
||||
cerr << msg << "\n";
|
||||
else
|
||||
syslog(LOG_MAKEPRI(LOG_USER, static_cast<int>(level)), "%s", msg.c_str());
|
||||
void log(const LogLevel level, const string&& msg) {
|
||||
if (dry_run)
|
||||
cerr << msg << "\n";
|
||||
else
|
||||
syslog(LOG_MAKEPRI(LOG_USER, static_cast<int>(level)), "%s", msg.c_str());
|
||||
}
|
||||
|
||||
/** Entry point for the application.
|
||||
@@ -293,49 +288,46 @@ void log(const LogLevel level, const string&& msg)
|
||||
@param argc The number of command-line arguments
|
||||
@param argv Command-line arguments
|
||||
*/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
static_assert(sizeof(unsigned long long) == 8,
|
||||
"wait, what kind of system is this?");
|
||||
parse_options(argc, argv);
|
||||
int main(int argc, char* argv[]) {
|
||||
static_assert(sizeof(unsigned long long) == 8,
|
||||
"wait, what kind of system is this?");
|
||||
parse_options(argc, argv);
|
||||
|
||||
if (!dry_run)
|
||||
daemonize();
|
||||
if (!dry_run) daemonize();
|
||||
|
||||
load_hashes();
|
||||
load_hashes();
|
||||
|
||||
// The following line helps avoid zombie processes. Normally parents
|
||||
// need to reap their children in order to prevent zombie processes;
|
||||
// if SIGCHLD is set to SIG_IGN, though, the processes can terminate
|
||||
// normally.
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
// The following line helps avoid zombie processes. Normally parents
|
||||
// need to reap their children in order to prevent zombie processes;
|
||||
// if SIGCHLD is set to SIG_IGN, though, the processes can terminate
|
||||
// normally.
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
if (dry_run)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
tcp::endpoint endpoint(tcp::v4(), port);
|
||||
tcp::acceptor acceptor(io_service, endpoint);
|
||||
if (dry_run) return EXIT_SUCCESS;
|
||||
|
||||
while (true) {
|
||||
tcp::iostream stream;
|
||||
boost::system::error_code error;
|
||||
acceptor.accept(*stream.rdbuf(), error);
|
||||
|
||||
if (error) {
|
||||
continue;
|
||||
}
|
||||
string ipaddr = stream.socket().remote_endpoint().address().to_string();
|
||||
log(LogLevel::ALERT, string("accepted a client: ") + ipaddr);
|
||||
boost::asio::io_service io_service;
|
||||
tcp::endpoint endpoint(tcp::v4(), port);
|
||||
tcp::acceptor acceptor(io_service, endpoint);
|
||||
|
||||
if (0 == fork()) {
|
||||
log(LogLevel::ALERT, "calling handle_client");
|
||||
handle_client(stream);
|
||||
return 0;
|
||||
}
|
||||
while (true) {
|
||||
tcp::iostream stream;
|
||||
boost::system::error_code error;
|
||||
acceptor.accept(*stream.rdbuf(), error);
|
||||
|
||||
if (error) {
|
||||
continue;
|
||||
}
|
||||
string ipaddr = stream.socket().remote_endpoint().address().to_string();
|
||||
log(LogLevel::ALERT, string("accepted a client: ") + ipaddr);
|
||||
|
||||
// Note that as is normal for daemons, the exit point is never
|
||||
// reached. This application does not normally terminate.
|
||||
return EXIT_SUCCESS;
|
||||
if (0 == fork()) {
|
||||
log(LogLevel::ALERT, "calling handle_client");
|
||||
handle_client(stream);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that as is normal for daemons, the exit point is never
|
||||
// reached. This application does not normally terminate.
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -17,18 +17,17 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <syslog.h>
|
||||
#include <cstdint>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
// Note: C++11 guarantees an unsigned long long will be at least 64 bits.
|
||||
// A compile-time assert in main.cc guarantees it will ONLY be 64 bits.
|
||||
using pair64 = std::pair<unsigned long long, unsigned long long>;
|
||||
|
||||
enum class LogLevel
|
||||
{
|
||||
enum class LogLevel {
|
||||
INFO = LOG_INFO,
|
||||
WARN = LOG_WARNING,
|
||||
DEBUG = LOG_DEBUG,
|
||||
|
||||
@@ -14,65 +14,55 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
#include "main.h"
|
||||
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using std::hex;
|
||||
using std::invalid_argument;
|
||||
using std::make_pair;
|
||||
using std::pair;
|
||||
using std::regex;
|
||||
using std::regex_match;
|
||||
using std::invalid_argument;
|
||||
using std::stringstream;
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
using std::hex;
|
||||
using std::setw;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
string
|
||||
from_pair64(const pair64& input)
|
||||
{
|
||||
stringstream stream;
|
||||
stream << setfill('0')
|
||||
<< setw(sizeof(unsigned long long) * 2)
|
||||
<< hex
|
||||
<< input.first
|
||||
<< input.second;
|
||||
return string(stream.str());
|
||||
string from_pair64(const pair64& input) {
|
||||
stringstream stream;
|
||||
stream << setfill('0') << setw(sizeof(unsigned long long) * 2) << hex
|
||||
<< input.first << input.second;
|
||||
return string(stream.str());
|
||||
}
|
||||
|
||||
pair64
|
||||
to_pair64(const string& input)
|
||||
{
|
||||
static const regex md5_re{ "^[A-Fa-f0-9]{32}$" };
|
||||
pair64 to_pair64(const string& input) {
|
||||
static const regex md5_re{"^[A-Fa-f0-9]{32}$"};
|
||||
|
||||
if (!regex_match(input.cbegin(), input.cend(), md5_re))
|
||||
throw invalid_argument("not a hash");
|
||||
|
||||
auto first = string(input.cbegin(), input.cbegin() + 16);
|
||||
auto second = string(input.cbegin() + 16, input.cend());
|
||||
auto left = std::strtoull(first.c_str(), nullptr, 16);
|
||||
auto right = std::strtoull(second.c_str(), nullptr, 16);
|
||||
if (!regex_match(input.cbegin(), input.cend(), md5_re))
|
||||
throw invalid_argument("not a hash");
|
||||
|
||||
return make_pair(left, right);
|
||||
auto first = string(input.cbegin(), input.cbegin() + 16);
|
||||
auto second = string(input.cbegin() + 16, input.cend());
|
||||
auto left = std::strtoull(first.c_str(), nullptr, 16);
|
||||
auto right = std::strtoull(second.c_str(), nullptr, 16);
|
||||
|
||||
return make_pair(left, right);
|
||||
}
|
||||
|
||||
bool operator<(const pair64& lhs, const pair64& rhs)
|
||||
{
|
||||
return (lhs.first < rhs.first) or
|
||||
(lhs.first == rhs.first and lhs.second < rhs.second);
|
||||
bool operator<(const pair64& lhs, const pair64& rhs) {
|
||||
return (lhs.first < rhs.first) or
|
||||
(lhs.first == rhs.first and lhs.second < rhs.second);
|
||||
}
|
||||
|
||||
bool operator==(const pair64& lhs, const pair64& rhs)
|
||||
{
|
||||
return (lhs.first == rhs.first) and (lhs.second == rhs.second);
|
||||
bool operator==(const pair64& lhs, const pair64& rhs) {
|
||||
return (lhs.first == rhs.first) and (lhs.second == rhs.second);
|
||||
}
|
||||
|
||||
bool operator>(const pair64& lhs, const pair64& rhs)
|
||||
{
|
||||
return ((!(lhs < rhs)) and (!(lhs == rhs)));
|
||||
bool operator>(const pair64& lhs, const pair64& rhs) {
|
||||
return ((!(lhs < rhs)) and (!(lhs == rhs)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user