Multithreading & UX update
This commit is contained in:
committed by
Brendan Le Glaunec
parent
de757e848d
commit
509d68f023
@@ -56,7 +56,7 @@ configuration::load_ids() {
|
||||
"the default one "
|
||||
"instead.",
|
||||
"configuration");
|
||||
content = read_file(default_ids_file_path_).second;
|
||||
content = read_file(default_rtsp_ids_file).second;
|
||||
}
|
||||
if (content.size()) {
|
||||
auto root = Json::Value();
|
||||
@@ -101,7 +101,7 @@ configuration::load_url() {
|
||||
"the default one "
|
||||
"instead.",
|
||||
"configuration");
|
||||
content = read_file(default_urls_file_path_).second;
|
||||
content = read_file(default_rtsp_url_file).second;
|
||||
}
|
||||
if (content.size()) {
|
||||
auto root = Json::Value();
|
||||
@@ -131,18 +131,47 @@ serialize(const Json::Value& root) {
|
||||
std::pair<bool, configuration> ret;
|
||||
|
||||
try {
|
||||
ret.second.ports = root["ports"].asString();
|
||||
ret.second.subnets = root["subnets"].asString();
|
||||
ret.second.rtsp_ids_file = root["rtsp_ids_file"].asString();
|
||||
ret.second.rtsp_url_file = root["rtsp_url_file"].asString();
|
||||
ret.second.thumbnail_storage_path = root["thumbnail_storage_path"].asString();
|
||||
ret.second.cache_manager_path = root["cache_manager_path"].asString();
|
||||
ret.second.cache_manager_name = root["cache_manager_name"].asString();
|
||||
if (!root["ports"].isNull())
|
||||
ret.second.ports = root["ports"].asString();
|
||||
else
|
||||
ret.second.ports = default_ports;
|
||||
|
||||
if (!root["subnets"].isNull())
|
||||
ret.second.subnets = root["subnets"].asString();
|
||||
else
|
||||
ret.second.subnets = default_subnets;
|
||||
|
||||
if (!root["rtsp_ids_file"].isNull())
|
||||
ret.second.rtsp_ids_file = root["rtsp_ids_file"].asString();
|
||||
else
|
||||
ret.second.rtsp_ids_file = default_rtsp_ids_file;
|
||||
|
||||
if (!root["rtsp_url_file"].isNull())
|
||||
ret.second.rtsp_url_file = root["rtsp_url_file"].asString();
|
||||
else
|
||||
ret.second.rtsp_url_file = default_rtsp_url_file;
|
||||
|
||||
if (!root["thumbnail_storage_path"].isNull())
|
||||
ret.second.thumbnail_storage_path = root["thumbnail_storage_path"].asString();
|
||||
else
|
||||
ret.second.thumbnail_storage_path = default_thumbnail_storage_path;
|
||||
|
||||
if (!root["cache_manager_path"].isNull())
|
||||
ret.second.cache_manager_path = root["cache_manager_path"].asString();
|
||||
else
|
||||
ret.second.cache_manager_path = default_cache_manager_path;
|
||||
|
||||
if (!root["cache_manager_name"].isNull())
|
||||
ret.second.cache_manager_name = root["cache_manager_name"].asString();
|
||||
else
|
||||
ret.second.cache_manager_name = default_cache_manager_name;
|
||||
|
||||
ret.first = true;
|
||||
} catch (std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERR_("Configuration failed : " + std::string(e.what()), "configuration");
|
||||
ret.first = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -156,7 +185,16 @@ configuration::get_raw() const {
|
||||
// Will return true & valid configuration if success
|
||||
// Otherwise false & empty configuration
|
||||
std::pair<bool, configuration>
|
||||
load(const std::string& path) {
|
||||
load(const std::pair<bool, etix::tool::opt_parse>& args) {
|
||||
std::string path;
|
||||
|
||||
if (not args.second.exist("-c")) {
|
||||
path = etix::cameradar::default_configuration_path;
|
||||
LOG_WARN_("No custom path set, trying to use default path: " + path, "main");
|
||||
} else {
|
||||
path = args.second["-c"];
|
||||
}
|
||||
|
||||
// Check if the file exists at the given path
|
||||
if (access(path.c_str(), F_OK) == -1) {
|
||||
LOG_ERR_("Can't access: " + path, "configuration");
|
||||
@@ -191,6 +229,9 @@ load(const std::string& path) {
|
||||
conf.first &= conf.second.load_url();
|
||||
conf.first &= conf.second.load_ids();
|
||||
|
||||
if (args.second.exist("-s")) conf.second.subnets = args.second["-s"];
|
||||
if (args.second.exist("-p")) conf.second.ports = args.second["-p"];
|
||||
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,19 @@
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Sends a request to the camera using the OPTION method,
|
||||
//! then a DESCRIBE to check for valid IDs
|
||||
//! then another DESCIBE with IDs if an authentication is needed
|
||||
// Ugly workaround
|
||||
size_t
|
||||
write_data(void* buffer, size_t size, size_t nmemb, void* userp) {
|
||||
// I'm sorry for this
|
||||
// Forget you ever saw it
|
||||
(void)buffer;
|
||||
(void)userp;
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
// Sends a request to the camera using the OPTION method,
|
||||
// then a DESCRIBE to check for valid IDs
|
||||
// then another DESCIBE with IDs if an authentication is needed
|
||||
bool
|
||||
curl_describe(const std::string& path, bool logs) {
|
||||
CURL* csession;
|
||||
@@ -27,23 +37,21 @@ curl_describe(const std::string& path, bool logs) {
|
||||
struct curl_slist* custom_msg = NULL;
|
||||
char URL[256];
|
||||
long rc;
|
||||
FILE* protofile = NULL;
|
||||
protofile = fopen("/dev/null", "wb");
|
||||
csession = curl_easy_init();
|
||||
if (csession == NULL) return -1;
|
||||
sprintf(URL, "%s", path.c_str());
|
||||
// These are the options for all following cURL requests
|
||||
// Activate verbose if debug is needed
|
||||
curl_easy_setopt(csession, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(csession, CURLOPT_TIMEOUT, 1);
|
||||
curl_easy_setopt(csession, CURLOPT_NOBODY, 1);
|
||||
curl_easy_setopt(csession, CURLOPT_URL, URL);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_STREAM_URI, URL);
|
||||
curl_easy_setopt(csession, CURLOPT_FOLLOWLOCATION, 0);
|
||||
curl_easy_setopt(csession, CURLOPT_HEADER, 0);
|
||||
curl_easy_setopt(csession, CURLOPT_INTERLEAVEDATA, protofile);
|
||||
curl_easy_setopt(csession, CURLOPT_VERBOSE, 0);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEFUNCTION, write_data);
|
||||
// This request will handshake the stream's server, it should always return 200 OK
|
||||
curl_easy_perform(csession);
|
||||
curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
@@ -51,11 +59,7 @@ curl_describe(const std::string& path, bool logs) {
|
||||
custom_msg, "Accept: application/x-rtsp-mh, application/rtsl, application/sdp");
|
||||
curl_easy_setopt(csession, CURLOPT_RTSPHEADER, custom_msg);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
// This request will check if the given path is right without the need of encrypted ids
|
||||
curl_easy_perform(
|
||||
csession); // will return 404 if no ids and bad route, 401 if ids, 200 is all ok
|
||||
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
unsigned long pos = path.find("@");
|
||||
if (pos != std::string::npos) {
|
||||
std::string encoded = etix::tool::encode::encode64(path.substr(7, pos - 7));
|
||||
@@ -63,14 +67,17 @@ curl_describe(const std::string& path, bool logs) {
|
||||
curl_slist_append(custom_msg, std::string("Authorization: Basic " + encoded).c_str());
|
||||
curl_easy_setopt(csession, CURLOPT_RTSPHEADER, custom_msg);
|
||||
curl_easy_setopt(csession, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE);
|
||||
curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
// curl_easy_setopt(csession, CURLOPT_WRITEDATA, protofile);
|
||||
// This request will check if the given ids are good
|
||||
curl_easy_perform(csession); // will return 404 if good ids, 401 if bad ids
|
||||
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
} else {
|
||||
curl_easy_perform(
|
||||
csession); // will return 404 if no ids and bad route, 401 if ids, 200 is all ok
|
||||
res = curl_easy_getinfo(csession, CURLINFO_RESPONSE_CODE, &rc);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(csession);
|
||||
fclose(protofile);
|
||||
curl_global_cleanup();
|
||||
LOG_DEBUG_("Response code : " + std::to_string(rc), "describe");
|
||||
if (logs) {
|
||||
// Some cameras return 400 instead of 401, don't know why.
|
||||
|
||||
@@ -54,8 +54,8 @@ dispatcher::run() {
|
||||
worker.join();
|
||||
}
|
||||
|
||||
//! This loop is used to add all the tasks specified in the command line
|
||||
//! And then run them successively
|
||||
// This loop is used to add all the tasks specified in the command line
|
||||
// And then run them successively
|
||||
void
|
||||
dispatcher::do_stuff() {
|
||||
if (opts.second.exist("-d")) {
|
||||
|
||||
@@ -88,12 +88,12 @@ create_recursive_folder(const std::string& folder) {
|
||||
|
||||
std::string
|
||||
get_file_folder(std::string full_file_path) {
|
||||
//! remove ending slash
|
||||
// remove ending slash
|
||||
if (full_file_path.back() == '/') full_file_path.pop_back();
|
||||
|
||||
size_t last_slash_position = full_file_path.find_last_of('/');
|
||||
|
||||
//! it there is no slash, there is no folder to return
|
||||
// it there is no slash, there is no folder to return
|
||||
if (last_slash_position == std::string::npos) return "";
|
||||
|
||||
return std::string(full_file_path, 0, last_slash_position);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Launches a command and checks for the return value
|
||||
// Launches a command and checks for the return value
|
||||
bool
|
||||
launch_command(const std::string& cmd) {
|
||||
int status = system(cmd.c_str());
|
||||
|
||||
@@ -24,7 +24,6 @@ void
|
||||
print_version() {
|
||||
std::cout << "Cameradar version " << CAMERADAR_VERSION << std::endl;
|
||||
std::cout << "Build " << CAMERADAR_VERSION_BUILD << std::endl;
|
||||
std::cout << "Git commit " << CAMERADAR_VERSION_GIT_SHA1 << std::endl;
|
||||
}
|
||||
|
||||
// Command line parsing
|
||||
@@ -32,6 +31,8 @@ std::pair<bool, etix::tool::opt_parse>
|
||||
parse_cmdline(int argc, char* argv[]) {
|
||||
auto opt_parse = etix::tool::opt_parse{ argc, argv };
|
||||
|
||||
opt_parse.optional("-s", "Set subnets (e.g.: `172.16.0.0/24`)", true);
|
||||
opt_parse.optional("-p", "Set ports (e.g.: `554,8554`)", true);
|
||||
opt_parse.optional("-c", "Path to the configuration file (-c /path/to/conf)", true);
|
||||
opt_parse.optional("-l", "Set log level (-l 4 will only show warnings and errors)", true);
|
||||
opt_parse.optional("-d", "Launch the discovery tool on the given subnet", false);
|
||||
@@ -86,18 +87,10 @@ main(int argc, char* argv[]) {
|
||||
if (not args.first) return EXIT_FAILURE;
|
||||
|
||||
print_version();
|
||||
// configure file configuration path
|
||||
auto conf_path = std::string{};
|
||||
if (not args.second.exist("-c")) {
|
||||
conf_path = etix::cameradar::default_configuration_path;
|
||||
LOG_WARN_("No custom path set, trying to use default path: " + conf_path, "main");
|
||||
} else {
|
||||
conf_path = args.second["-c"];
|
||||
}
|
||||
|
||||
if (not args.second.exist("-l")) {
|
||||
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::INFO);
|
||||
LOG_INFO_("No log level set, using log level 2 (ignoring DEBUG)", "main");
|
||||
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::DEBUG);
|
||||
LOG_INFO_("No log level set, using log level 1", "main");
|
||||
} else {
|
||||
try {
|
||||
int level = std::stoi(args.second["-l"]);
|
||||
@@ -110,7 +103,7 @@ main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Try to load the configuration
|
||||
auto conf = cmrdr::load(conf_path);
|
||||
auto conf = cmrdr::load(args);
|
||||
if (not conf.first) { return EXIT_FAILURE; }
|
||||
|
||||
LOG_INFO_("Configuration successfully loaded", "main");
|
||||
|
||||
@@ -25,9 +25,9 @@ static const std::string no_ids_warning_ =
|
||||
"default routes. "
|
||||
"Path bruteforce is impossible without the IDs.";
|
||||
|
||||
//! Tries to match the detected combination of Username / Password
|
||||
//! with the camera stream. Creates a resource in the DB upon
|
||||
//! valid discovery
|
||||
// Tries to match the detected combination of Username / Password
|
||||
// with the camera stream. Creates a resource in the DB upon
|
||||
// valid discovery
|
||||
bool
|
||||
brutelogs::test_ids(const etix::cameradar::stream_model& stream,
|
||||
const std::string& password,
|
||||
@@ -36,22 +36,24 @@ brutelogs::test_ids(const etix::cameradar::stream_model& stream,
|
||||
std::string path = stream.service_name + "://";
|
||||
if (username != "" || password != "") { path += username + ":" + password + "@"; }
|
||||
path += stream.address + ":" + std::to_string(stream.port);
|
||||
LOG_DEBUG_("Testing ids : " + path, "brutelogs");
|
||||
LOG_INFO_("Testing ids : " + path, "brutelogs");
|
||||
try {
|
||||
if (curl_describe(path, true)) {
|
||||
LOG_DEBUG_("[FOUND IDS] : " + path, "brutelogs");
|
||||
LOG_INFO_("[FOUND IDS] : " + path, "brutelogs");
|
||||
found = true;
|
||||
stream_model newstream{
|
||||
stream.address, stream.port, username, password,
|
||||
stream.route, stream.service_name, stream.product, stream.protocol,
|
||||
stream.state, true, stream.path_found, stream.thumbnail_path
|
||||
stream.address, stream.port, username, password,
|
||||
stream.route, stream.service_name, stream.product, stream.protocol,
|
||||
stream.state, stream.path_found, true, stream.thumbnail_path
|
||||
};
|
||||
if ((*cache)->has_changed(stream)) return true;
|
||||
(*cache)->update_stream(newstream);
|
||||
} else {
|
||||
stream_model newstream{ stream.address, stream.port, username,
|
||||
password, stream.route, stream.service_name,
|
||||
stream.product, stream.protocol, stream.state,
|
||||
false, stream.path_found, stream.thumbnail_path };
|
||||
stream_model newstream{ stream.address, stream.port, username,
|
||||
password, stream.route, stream.service_name,
|
||||
stream.product, stream.protocol, stream.state,
|
||||
stream.path_found, false, stream.thumbnail_path };
|
||||
if ((*cache)->has_changed(stream)) return true;
|
||||
(*cache)->update_stream(newstream);
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
@@ -63,25 +65,45 @@ brutelogs::test_ids(const etix::cameradar::stream_model& stream,
|
||||
bool
|
||||
ids_already_found(std::vector<stream_model> streams, stream_model stream) {
|
||||
for (const auto& it : streams) {
|
||||
if ((stream.address == it.address) && (stream.port == it.port) && it.ids_found) return true;
|
||||
if ((stream.address == it.address) && (stream.port == it.port) && it.ids_found) {
|
||||
LOG_DEBUG_(">>>>>> PATH " + std::to_string(stream.path_found) + " IDS " +
|
||||
std::to_string(stream.ids_found),
|
||||
"wtf");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Tries to discover the right IDs on all RTSP streams in DB
|
||||
//! Uses the ids.json file to try different combinations
|
||||
bool
|
||||
brutelogs::bruteforce_camera(const stream_model& stream) const {
|
||||
for (const auto& username : conf.usernames) {
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
for (const auto& password : conf.passwords) {
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if ((*cache)->has_changed(stream)) return true;
|
||||
if (test_ids(stream, password, username)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tries to discover the right IDs on all RTSP streams in DB
|
||||
// Uses the ids.json file to try different combinations
|
||||
bool
|
||||
brutelogs::run() const {
|
||||
std::vector<std::future<bool>> futures;
|
||||
|
||||
LOG_INFO_(
|
||||
"Beginning bruteforce of the usernames and passwords task, it may "
|
||||
"take a while.",
|
||||
"brutelogs");
|
||||
std::vector<etix::cameradar::stream_model> streams = (*cache)->get_streams();
|
||||
LOG_DEBUG_("Found " + std::to_string(streams.size()) + " streams in the cache", "brutelogs");
|
||||
bool doubleskip;
|
||||
size_t found = 0;
|
||||
for (const auto& stream : streams) {
|
||||
doubleskip = false;
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if ((found < streams.size()) && ids_already_found(streams, stream)) {
|
||||
@@ -92,24 +114,13 @@ brutelogs::run() const {
|
||||
"brutelogs");
|
||||
++found;
|
||||
} else {
|
||||
for (const auto& username : conf.usernames) {
|
||||
if (doubleskip ||
|
||||
signal_handler::instance().should_stop() !=
|
||||
etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
for (const auto& password : conf.passwords) {
|
||||
if (doubleskip ||
|
||||
signal_handler::instance().should_stop() !=
|
||||
etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if (test_ids(stream, password, username)) {
|
||||
++found;
|
||||
doubleskip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, &brutelogs::bruteforce_camera, this, stream));
|
||||
}
|
||||
}
|
||||
for (auto& fit : futures) {
|
||||
if (fit.get()) { ++found; }
|
||||
}
|
||||
if (!found) {
|
||||
LOG_WARN_(no_ids_warning_, "brutelogs");
|
||||
return false;
|
||||
|
||||
@@ -19,9 +19,9 @@ static const std::string no_route_found_ =
|
||||
"default "
|
||||
"routes. Thumbnail generation is impossible without the path.";
|
||||
|
||||
//! Tries to match the detected combination of Username / Password
|
||||
//! with a route for the camera stream. Creates a resource in the DB upon
|
||||
//! valid discovery
|
||||
// Tries to match the detected combination of Username / Password
|
||||
// with a route for the camera stream. Creates a resource in the DB upon
|
||||
// valid discovery
|
||||
bool
|
||||
brutepath::test_path(const stream_model& stream, const std::string& route) const {
|
||||
bool found = false;
|
||||
@@ -29,7 +29,7 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
|
||||
stream.address + ":" + std::to_string(stream.port);
|
||||
if (route.front() != '/') { path += "/"; }
|
||||
path += route;
|
||||
LOG_DEBUG_("Testing path : " + path, "brutepath");
|
||||
LOG_INFO_("Testing path : " + path, "brutepath");
|
||||
try {
|
||||
if (curl_describe(path, false)) {
|
||||
// insert in DB and go to the next port, print a cool message
|
||||
@@ -40,6 +40,7 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
|
||||
stream.service_name, stream.product, stream.protocol, stream.state, true,
|
||||
stream.ids_found, stream.thumbnail_path
|
||||
};
|
||||
if ((*cache)->has_changed(stream)) return true;
|
||||
(*cache)->update_stream(newstream);
|
||||
} else {
|
||||
stream_model newstream{
|
||||
@@ -47,6 +48,7 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
|
||||
stream.service_name, stream.product, stream.protocol, stream.state, false,
|
||||
stream.ids_found, stream.thumbnail_path
|
||||
};
|
||||
if ((*cache)->has_changed(stream)) return true;
|
||||
(*cache)->update_stream(newstream);
|
||||
}
|
||||
} catch (const std::runtime_error& e) { LOG_INFO_(e.what(), "brutepath"); }
|
||||
@@ -56,16 +58,36 @@ brutepath::test_path(const stream_model& stream, const std::string& route) const
|
||||
bool
|
||||
path_already_found(std::vector<stream_model> streams, stream_model model) {
|
||||
for (const auto& stream : streams) {
|
||||
if ((model.address == stream.address) && (model.port == stream.port) && stream.path_found)
|
||||
if ((model.address == stream.address) && (model.port == stream.port) && stream.path_found) {
|
||||
LOG_DEBUG_(">>>>>> PATH " + std::to_string(stream.path_found) + " IDS " +
|
||||
std::to_string(stream.ids_found),
|
||||
"wtf");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//! Tries to discover a route on all RTSP streams in DB
|
||||
//! Uses the url.json file to try different routes
|
||||
bool
|
||||
brutepath::bruteforce_camera(const stream_model& stream) const {
|
||||
for (const auto& route : conf.paths) {
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if ((*cache)->has_changed(stream)) return true;
|
||||
if (test_path(stream, route)) {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tries to discover a route on all RTSP streams in DB
|
||||
// Uses the url.json file to try different routes
|
||||
bool
|
||||
brutepath::run() const {
|
||||
std::vector<std::future<bool>> futures;
|
||||
|
||||
LOG_INFO_("Beginning bruteforce of the camera paths task, it may take a while.", "bruteforce");
|
||||
std::vector<stream_model> streams = (*cache)->get_streams();
|
||||
int found = 0;
|
||||
@@ -74,22 +96,18 @@ brutepath::run() const {
|
||||
break;
|
||||
if (path_already_found(streams, stream)) {
|
||||
LOG_INFO_(stream.address +
|
||||
" : This camera's path was already discovered in the database."
|
||||
"Skipping to the next camera.",
|
||||
" : This camera's path was already discovered in the database. Skipping "
|
||||
"to the next camera.",
|
||||
"brutepath");
|
||||
++found;
|
||||
} else {
|
||||
for (const auto& route : conf.paths) {
|
||||
if (signal_handler::instance().should_stop() !=
|
||||
etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
if (test_path(stream, route)) {
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, &brutepath::bruteforce_camera, this, stream));
|
||||
}
|
||||
}
|
||||
for (auto& fit : futures) {
|
||||
if (fit.get()) { ++found; }
|
||||
}
|
||||
if (!found) {
|
||||
LOG_WARN_(no_route_found_, "brutepath");
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! The first command checks if dpkg finds nmap in the system by cutting the
|
||||
//! result and grepping
|
||||
//! nmap from it.
|
||||
//!
|
||||
//! The second command checks the version of nmap, right now it needs to be the
|
||||
//! 6.47 but this could
|
||||
//! be changed to 6 or greater depending on the needs. In a docker container
|
||||
//! this should not be a
|
||||
//! problem.
|
||||
// The first command checks if dpkg finds nmap in the system by cutting the
|
||||
// result and grepping
|
||||
// nmap from it.
|
||||
//
|
||||
// The second command checks the version of nmap, right now it needs to be the
|
||||
// 6.47 but this could
|
||||
// be changed to 6 or greater depending on the needs. In a docker container
|
||||
// this should not be a
|
||||
// problem.
|
||||
bool
|
||||
nmap_is_ok() {
|
||||
return (
|
||||
@@ -33,8 +33,8 @@ nmap_is_ok() {
|
||||
&& launch_command("mkdir -p /tmp/scans")); // Creates the directory in which the scans will be stored
|
||||
}
|
||||
|
||||
//! Launches and checks the return of the nmap command
|
||||
//! Uses the subnets specified in the conf file to launch nmap
|
||||
// Launches and checks the return of the nmap command
|
||||
// Uses the subnets specified in the conf file to launch nmap
|
||||
bool
|
||||
mapping::run() const {
|
||||
if (nmap_is_ok()) {
|
||||
|
||||
@@ -24,7 +24,7 @@ static const std::string no_hosts_found_ =
|
||||
"were "
|
||||
"accessible";
|
||||
|
||||
//! Avoids segfaults on unknown xml structure
|
||||
// Avoids segfaults on unknown xml structure
|
||||
std::string
|
||||
xml_safe_get(const TiXmlElement* elem, const std::string& attr) {
|
||||
if (elem == nullptr) return "closed";
|
||||
@@ -32,8 +32,8 @@ xml_safe_get(const TiXmlElement* elem, const std::string& attr) {
|
||||
return "closed";
|
||||
}
|
||||
|
||||
//! Parse a single host node (generally containing only one camera)
|
||||
//! Pushes it back to the data structure
|
||||
// Parse a single host node (generally containing only one camera)
|
||||
// Pushes it back to the data structure
|
||||
void
|
||||
parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) const {
|
||||
TiXmlElement* xml_streams = xml_host->FirstChild("ports")->ToElement();
|
||||
@@ -58,8 +58,8 @@ parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) c
|
||||
}
|
||||
}
|
||||
|
||||
//! Prints all detected cameras into the data structure and stops the program if
|
||||
//! no open RTSP streams were found
|
||||
// Prints all detected cameras into the data structure and stops the program if
|
||||
// no open RTSP streams were found
|
||||
bool
|
||||
parsing::print_detected_cameras(const std::vector<stream_model>& data) const {
|
||||
int added = 0;
|
||||
@@ -90,8 +90,8 @@ parsing::print_detected_cameras(const std::vector<stream_model>& data) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Opens the nmap output file, parses the data of each discovered port
|
||||
//! Adds the RTSP ports only into the DB
|
||||
// Opens the nmap output file, parses the data of each discovered port
|
||||
// Adds the RTSP ports only into the DB
|
||||
bool
|
||||
parsing::run() const {
|
||||
std::vector<stream_model> data;
|
||||
@@ -113,7 +113,7 @@ parsing::run() const {
|
||||
LOG_WARN_(no_hosts_found_, "parsing");
|
||||
if (data.size() == 0) { LOG_WARN_("No cameras were discovered", "parsing"); }
|
||||
return print_detected_cameras(data);
|
||||
} catch (std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERR_("Error during parsing. brutepath aborted : " + std::string(e.what()), "parsing");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Launches and checks the return of the nmap command
|
||||
//! Uses the subnets specified in the conf file to launch nmap
|
||||
// Launches and checks the return of the nmap command
|
||||
// Uses the subnets specified in the conf file to launch nmap
|
||||
bool
|
||||
print::run() const {
|
||||
std::vector<stream_model> results = (*cache)->get_valid_streams();
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
namespace etix {
|
||||
namespace cameradar {
|
||||
|
||||
//! Gets all the discovered streams with good routes and logs
|
||||
//! And launches an ffmpeg command to generate a thumbnail
|
||||
//! In order to check for the stream validity
|
||||
// Gets all the discovered streams with good routes and logs
|
||||
// And launches an ffmpeg command to generate a thumbnail
|
||||
// In order to check for the stream validity
|
||||
bool
|
||||
stream_check::run() const {
|
||||
GstElement* pipeline;
|
||||
@@ -30,8 +30,8 @@ stream_check::run() const {
|
||||
std::vector<stream_model> streams = (*cache)->get_valid_streams();
|
||||
|
||||
if (not streams.size()) {
|
||||
LOG_WARN_("There were no valid streams to check. Cameradar will stop.", "stream_check");
|
||||
return false;
|
||||
LOG_WARN_("There were no valid streams to check. Cameradar will stop.", "stream_check");
|
||||
return false;
|
||||
}
|
||||
for (const auto& stream : streams) {
|
||||
GError* error = NULL;
|
||||
@@ -40,13 +40,17 @@ stream_check::run() const {
|
||||
gst_parse_launch("rtspsrc name=source ! rtph264depay ! h264parse ! fakesink", &error);
|
||||
|
||||
std::string location = "rtsp://";
|
||||
location += stream.username + ":" + stream.password + "@" + stream.address + ":" + std::to_string(stream.port);
|
||||
location += stream.username + ":" + stream.password + "@" + stream.address + ":" +
|
||||
std::to_string(stream.port);
|
||||
if (pipeline == NULL) {
|
||||
LOG_ERR_("[" + stream.address + "] Can't configure pipeline", "stream_check");
|
||||
return false;
|
||||
} else {
|
||||
elem = gst_bin_get_by_name(GST_BIN(pipeline), "source");
|
||||
LOG_DEBUG_("Launching gstreamer check on rtsp://" + stream.username + ":" + stream.password + "@" + stream.address + ":" + std::to_string(stream.port), "gstreamer check");
|
||||
LOG_DEBUG_("Launching gstreamer check on rtsp://" + stream.username + ":" +
|
||||
stream.password + "@" + stream.address + ":" +
|
||||
std::to_string(stream.port),
|
||||
"gstreamer check");
|
||||
g_object_set(G_OBJECT(elem), "location", location.c_str(), "latency", 20, NULL);
|
||||
|
||||
if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
|
||||
@@ -63,7 +67,9 @@ stream_check::run() const {
|
||||
(*cache)->update_stream(invalidstream);
|
||||
return false;
|
||||
}
|
||||
LOG_INFO_("[" + stream.address + "] Set pipeline to playing", "stream_check");
|
||||
LOG_INFO_("[" + stream.address +
|
||||
"] This stream is accessible and seems to be functional",
|
||||
"stream_check");
|
||||
}
|
||||
}
|
||||
LOG_INFO_("All streams could be accessed with GStreamer", "stream_check");
|
||||
|
||||
@@ -41,61 +41,74 @@ thumbnail::build_output_file_path(const std::string& path) const {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! Gets all the discovered streams with good routes and logs
|
||||
//! And launches an ffmpeg command to generate a thumbnail
|
||||
//! In order to check for the stream validity
|
||||
bool
|
||||
thumbnail::generate_thumbnail(const stream_model& stream) const {
|
||||
LOG_INFO_("Generating thumbnail for " + stream.address, "thumbnail_generation");
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
return false;
|
||||
std::string ffmpeg_cmd =
|
||||
"mkdir -p %s ; "
|
||||
"ffmpeg "
|
||||
"-rtsp_transport tcp "
|
||||
"-y "
|
||||
"-nostdin "
|
||||
"-loglevel quiet " // no logs
|
||||
"-i '%s' " // input
|
||||
"-vcodec mjpeg " // jpeg codec
|
||||
"-vframes 1 " // only take one frame
|
||||
"-an " // disable audio
|
||||
"-f image2 " // force image
|
||||
"-s 240x180 " // force size
|
||||
"'%s'";
|
||||
std::string fullpath = make_path(stream);
|
||||
std::string output = build_output_file_path(stream.address);
|
||||
ffmpeg_cmd = tool::fmt(ffmpeg_cmd.c_str(),
|
||||
output.substr(0, output.find_last_of("/")).c_str(),
|
||||
fullpath.c_str(),
|
||||
output.c_str());
|
||||
if (!launch_command(ffmpeg_cmd)) {
|
||||
LOG_WARN_("The following command [" + ffmpeg_cmd +
|
||||
"] didn't work. That can either mean that the stream is "
|
||||
"not valid or "
|
||||
"that there is a problem with the camera.",
|
||||
"thumbnail_generation");
|
||||
return false;
|
||||
} else {
|
||||
LOG_DEBUG_("Generated thumbnail : " + ffmpeg_cmd, "thumbnail_generation");
|
||||
try {
|
||||
stream_model result{ stream.address, stream.port, stream.username,
|
||||
stream.password, stream.route, stream.service_name,
|
||||
stream.product, stream.protocol, stream.state,
|
||||
stream.path_found, stream.ids_found, output };
|
||||
(*cache)->update_stream(result);
|
||||
|
||||
} catch (const std::exception& e) { LOG_DEBUG_(e.what(), "thumbnail_generation"); }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets all the discovered streams with good routes and logs
|
||||
// And launches an ffmpeg command to generate a thumbnail
|
||||
// In order to check for the stream validity
|
||||
bool
|
||||
thumbnail::run() const {
|
||||
std::vector<std::future<bool>> futures;
|
||||
std::vector<stream_model> streams = (*cache)->get_valid_streams();
|
||||
|
||||
LOG_INFO_("Started thumbnail generation, it may take a while", "thumbnail");
|
||||
if (not streams.size()) {
|
||||
LOG_WARN_("There were no valid streams to generate thumbnails from. Cameradar will stop.", "thumbnail_generation");
|
||||
return false;
|
||||
LOG_WARN_("There were no valid streams to generate thumbnails from. Cameradar will stop.",
|
||||
"thumbnail_generation");
|
||||
return false;
|
||||
}
|
||||
int done = 0;
|
||||
for (const auto& stream : streams) {
|
||||
LOG_DEBUG_("Generating thumbnail for " + stream.address, "thumbnail_generation");
|
||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||
break;
|
||||
std::string ffmpeg_cmd =
|
||||
"mkdir -p %s ; "
|
||||
"ffmpeg "
|
||||
"-y "
|
||||
"-nostdin "
|
||||
"-loglevel quiet "
|
||||
"-i '%s' "
|
||||
"-vcodec mjpeg "
|
||||
"-vframes 1 "
|
||||
"-an "
|
||||
"-f image2 "
|
||||
"-s 320x240 "
|
||||
"'%s'";
|
||||
std::string fullpath = make_path(stream);
|
||||
std::string output = build_output_file_path(stream.address);
|
||||
ffmpeg_cmd = tool::fmt(ffmpeg_cmd.c_str(),
|
||||
output.substr(0, output.find_last_of("/")).c_str(),
|
||||
fullpath.c_str(),
|
||||
output.c_str());
|
||||
if (!launch_command(ffmpeg_cmd)) {
|
||||
LOG_WARN_("The following command [" + ffmpeg_cmd +
|
||||
"] didn't work. That can either mean that the stream is "
|
||||
"not valid or "
|
||||
"that there is a problem with the camera.",
|
||||
"thumbnail_generation");
|
||||
} else {
|
||||
LOG_DEBUG_("Generated thumbnail : " + ffmpeg_cmd, "thumbnail_generation");
|
||||
try {
|
||||
stream_model result{ stream.address, stream.port, stream.username,
|
||||
stream.password, stream.route, stream.service_name,
|
||||
stream.product, stream.protocol, stream.state,
|
||||
stream.path_found, stream.ids_found, output };
|
||||
(*cache)->update_stream(result);
|
||||
|
||||
} catch (std::exception& e) { LOG_DEBUG_(e.what(), "thumbnail_generation"); }
|
||||
}
|
||||
futures.push_back(
|
||||
std::async(std::launch::async, &thumbnail::generate_thumbnail, this, stream));
|
||||
}
|
||||
for (auto& fit : futures) {
|
||||
if (fit.get()) { ++done; }
|
||||
}
|
||||
LOG_INFO_("All thumbnails have been successfully generated in " +
|
||||
this->conf.thumbnail_storage_path,
|
||||
"thumbnail_generation");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user