Initial commit

This commit is contained in:
Brendan LE GLAUNEC
2016-05-20 16:13:22 +02:00
committed by Brendan LE GLAUNEC
parent 2af5f4475e
commit 201d7e31c6
88 changed files with 17719 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cachemanager.h" // for cache_manager
#include <dlfcn.h> // for dlerror, dlclose, dlopen, dlsym, etc
#include <logger.h> // for LOG_ERR_
#include <stdbool.h> // for bool, false, true
#include <algorithm> // for move
#include <errno.h>
namespace etix {
namespace cameradar {
#ifdef __APPLE__
const std::string cache_manager::PLUGIN_EXT = ".dylib";
#elif __linux__
const std::string cache_manager::PLUGIN_EXT = ".so";
#endif
const std::string cache_manager::default_symbol = "cache_manager_instance_new";
cache_manager::cache_manager(const std::string& path,
const std::string& name,
const std::string& symbol)
: name(name), path(path), symbol(symbol), handle(nullptr), ptr(nullptr) {}
cache_manager::cache_manager(cache_manager&& old)
: path(std::move(old.path)), symbol(std::move(old.symbol)) {
this->handle = old.handle;
old.handle = nullptr;
this->ptr = old.ptr;
old.ptr = nullptr;
}
cache_manager::~cache_manager() {
if (this->ptr) {
delete this->ptr;
this->ptr = nullptr;
}
if (this->handle) { dlclose(handle); }
}
bool
cache_manager::make_instance() {
cache_manager_iface* (*new_fn)() = nullptr;
// Gets the path to the dynamic library
auto real_path = this->make_full_path();
// Opens it to get the handle
this->handle = dlopen(real_path.c_str(), RTLD_LAZY);
if (this->handle == nullptr) {
std::cout << "error: " << dlerror() << std::endl;
LOG_ERR_("Failed to load cache manager: " + this->name + ", invalid path",
"cache manager loader");
return false;
} else {
// Gets the symbol and checks if the library is valid
*(void**)(&new_fn) = dlsym(this->handle, symbol.c_str());
if (dlerror() != nullptr) {
LOG_ERR_("Invalid cache manager package: " + this->name, "cache manager loader");
return false;
}
}
// Returns a string containing the most recent dl* error
dlerror();
// Instantiates the cache manager
this->ptr = (*new_fn)();
if (this->ptr == nullptr) {
LOG_ERR_("Invalid cache manager format: " + this->name, "cache manager loader");
return false;
}
return true;
}
// Generates a path as such : /libdumb_cache_manager.so
std::string
cache_manager::make_full_path() {
std::string full_path = this->path;
full_path += "/lib";
full_path += this->name;
full_path += "_cache_manager";
full_path += PLUGIN_EXT;
return full_path;
}
cache_manager_iface* cache_manager::operator->() { return this->ptr; }
const cache_manager_iface* cache_manager::operator->() const { return this->ptr; }
bool operator==(std::nullptr_t nullp, const cache_manager& p) { return p.ptr == nullp; }
bool operator==(const cache_manager& p, std::nullptr_t nullp) { return p.ptr == nullp; }
bool operator!=(std::nullptr_t nullp, const cache_manager& p) { return p.ptr != nullp; }
bool operator!=(const cache_manager& p, std::nullptr_t nullp) { return p.ptr != nullp; }
cache_manager_base&
cache_manager_base::get_instance() {
return *this;
}
} // cameradar
} // etix
+206
View File
@@ -0,0 +1,206 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fstream> // std::ifstream
#include <unistd.h> // access, F_OK
#include <configuration.h> // configuration
namespace etix {
namespace cameradar {
const std::string configuration::name_ = "configuration";
// read a file at the path "path"
// if the file is available we return the whole content as an std::string inside
// a pair
// otherwise return false and an empty string inside a pair
std::pair<bool, std::string>
read_file(const std::string& path) {
auto line = std::string{};
auto content = std::string{};
auto file = std::ifstream{ path };
if (file.is_open()) {
while (getline(file, line)) { content += line + "\n"; }
file.close();
} else {
return std::make_pair(false, std::string{});
}
return std::make_pair(true, content);
}
// Loads the IDS dictionary
bool
configuration::load_ids() {
std::string content;
LOG_DEBUG_("Trying to open ids file from " + this->rtsp_ids_file, "configuration");
if (this->rtsp_ids_file.size()) {
content = read_file(this->rtsp_ids_file.c_str()).second;
} else {
LOG_WARN_(
"No ids file detected in your configuration, Cameradar will use "
"the default one "
"instead.",
"configuration");
content = read_file(default_ids_file_path_).second;
}
if (content.size()) {
auto root = Json::Value();
auto reader = Json::Reader();
reader.parse(content, root);
for (unsigned int i = 0; i < root["username"].size(); i++) {
if (not root["username"][i].isString()) {
LOG_ERR_("\"username\" should be of type string", "configuration");
return false;
}
this->usernames.push_back(root["username"][i].asString());
}
for (unsigned int i = 0; i < root["password"].size(); i++) {
if (not root["password"][i].isString()) {
LOG_ERR_("\"password\" should be of type string", "configuration");
return false;
}
this->passwords.push_back(root["password"][i].asString());
}
return true;
} else {
LOG_ERR_(
"Could not load ids file. Make sure you provided a valid path in your "
"configuration file.",
"configuration");
return false;
}
}
// Loads the URL dictionary
bool
configuration::load_url() {
std::string content;
LOG_DEBUG_("Trying to open ids file from " + this->rtsp_ids_file, "configuration");
if (this->rtsp_url_file.size()) {
content = read_file(this->rtsp_url_file.c_str()).second;
} else {
LOG_WARN_(
"No ids file detected in your configuration, Cameradar will use "
"the default one "
"instead.",
"configuration");
content = read_file(default_urls_file_path_).second;
}
if (content.size()) {
auto root = Json::Value();
auto reader = Json::Reader();
reader.parse(content, root);
// auto result = tool::json::check_fields(
// {{"urls", Json::arrayValue, root["urls"]}}, "general
// configuration");
// if (not result.first) {
// LOG_ERR_(result.second, "general configuration");
// return false;
// }
for (unsigned int i = 0; i < root["urls"].size(); i++) {
if (not root["urls"][i].isString()) {
LOG_ERR_("\"urls\" should be of type string", "configuration");
return false;
}
this->paths.push_back(root["urls"][i].asString());
}
return true;
} else {
LOG_ERR_(
"Could not load ids file. Make sure you provided a valid path in your "
"configuration file.",
"configuration");
return false;
}
}
std::pair<bool, configuration>
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();
ret.first = true;
} catch (std::exception& e) {
LOG_ERR_("Configuration failed : " + std::string(e.what()), "configuration");
ret.first = false;
}
return ret;
}
Json::Value
configuration::get_raw() const {
return this->raw_conf;
}
// Loads the configuration from a path
// Returns a pair containing a boolean value & the configuration.
// Will return true & valid configuration if success
// Otherwise false & empty configuration
std::pair<bool, configuration>
load(const std::string& path) {
// Check if the file exists at the given path
if (access(path.c_str(), F_OK) == -1) {
LOG_ERR_("Can't access: " + path, "configuration");
return std::make_pair(false, configuration{});
}
// Get the content of the file
auto content = read_file(path);
if (not content.first) {
LOG_ERR_(
"Can't open configuration file, you should check your rights to "
"access the file",
"configuration");
return std::make_pair(false, configuration{});
}
// Parse & validate the json
auto root = Json::Value();
auto reader = Json::Reader();
auto parse_succes = reader.parse(content.second, root);
if (not parse_succes) {
LOG_ERR_("Can't load configuration, invalid json format:\n" +
reader.getFormattedErrorMessages(),
"configuration");
return std::make_pair(false, configuration{});
}
// Deserialize the json to a configuration struct
// and return
// REPLACE THIS WITH JSONCPP
std::pair<bool, configuration> conf = serialize(root);
conf.second.raw_conf = root;
conf.first &= conf.second.load_url();
conf.first &= conf.second.load_ids();
return conf;
}
}
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <describe.h>
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
bool
curl_describe(const std::string& path, bool logs) {
CURL* csession;
CURLcode res;
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_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);
// 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);
custom_msg = curl_slist_append(
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));
custom_msg =
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);
// 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);
}
curl_easy_cleanup(csession);
fclose(protofile);
curl_global_cleanup();
if (logs) {
if (rc != 401 && pos == std::string::npos)
LOG_INFO_("Unprotected camera discovered.", "brutelogs");
return ((res == CURLE_OK) && rc != 401);
}
return ((res == CURLE_OK) && rc != 404);
}
}
}
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dispatcher.h"
namespace etix {
namespace cameradar {
// The main loop of the binary
void
dispatcher::run() {
std::thread worker(&dispatcher::do_stuff, this);
using namespace std::chrono_literals;
// catch CTRL+C signal
signal_handler::instance();
// wait for event or end
while (signal_handler::instance().should_stop() not_eq stop_priority::stop &&
current != task::finished) {
std::this_thread::sleep_for(30ms);
}
if (doing_stuff()) {
LOG_INFO_("Waiting for a task to terminate", "dispatcher");
LOG_INFO_("Press CTRL+C again to force stop", "dispatcher");
}
// waiting for task to cleanup / force stop command
while ((signal_handler::instance().should_stop() not_eq stop_priority::force_stop) and
doing_stuff()) {
std::this_thread::sleep_for(std::chrono::milliseconds(30));
}
worker.join();
}
//! 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")) {
queue.push_back(new etix::cameradar::mapping(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
}
if (opts.second.exist("-b")) {
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
}
if (opts.second.exist("-t")) {
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
}
if (opts.second.exist("-g")) {
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
}
if (!opts.second.exist("-d") && !opts.second.exist("-b") && !opts.second.exist("-t") &&
!opts.second.exist("-g")) {
queue.push_back(new etix::cameradar::mapping(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
}
queue.push_back(new etix::cameradar::print(cache, conf, nmap_output));
while (queue.size() > 0 && signal_handler::instance().should_stop() == stop_priority::running) {
if (queue.front()->run())
queue.pop_front();
else {
LOG_ERR_("An error occured in one of the tasks, Cameradar will now stop.", "dispatcher");
break;
}
}
this->current = task::finished;
}
}
}
+124
View File
@@ -0,0 +1,124 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "encode.h"
#include <ctype.h>
namespace etix {
namespace tool {
namespace encode {
std::string
encode64(const std::string& str_to_encode) {
return base64_encode(reinterpret_cast<const unsigned char*>(str_to_encode.c_str()),
str_to_encode.length());
}
std::string
decode64(const std::string& str_to_decode) {
return base64_decode(str_to_decode);
}
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool
is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
/* from external source */
std::string
base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
while ((i++ < 3)) ret += '=';
}
return ret;
}
/* from external source */
std::string
base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_];
in_++;
if (i == 4) {
for (i = 0; i < 4; i++) char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++) ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) char_array_4[j] = 0;
for (j = 0; j < 4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
}
}
}
+124
View File
@@ -0,0 +1,124 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fs.h"
#include <vector> // for std::vector
#include <sstream> // for std::stringstream
#include <pwd.h> // for getpwuid, passwd
#include <stddef.h> // for size_t
#include <sys/stat.h> // for stat, mkdir, S_ISDIR
#include <unistd.h> // for getuid
#include <fstream> // for std::ifstream
namespace etix {
namespace tool {
std::vector<std::string>
split(const std::string& s, char delim) {
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) elems.push_back(item);
return elems;
}
namespace fs {
fs_error
is_folder(const std::string& folder) {
struct stat sb;
if (stat(folder.c_str(), &sb) == 0) {
if (S_ISDIR(sb.st_mode))
return fs_error::is_dir;
else
return fs_error::is_not_dir;
}
return fs_error::dont_exist;
}
bool
get_or_create_folder(const std::string& folder) {
bool status = false;
switch (is_folder(folder)) {
case fs_error::is_dir: status = true; break;
case fs_error::is_not_dir: status = false; break;
case fs_error::dont_exist: status = create_recursive_folder(folder); break;
}
return status;
}
bool
create_folder(const std::string& folder) {
if (mkdir(folder.c_str(), 0755) == 0) { return true; }
return false;
}
bool
create_recursive_folder(const std::string& folder) {
auto path_elems = split(folder, '/');
std::string current_path = folder[0] == '/' ? "/" : "";
for (const auto& elem : path_elems) {
current_path += elem;
if (is_folder(current_path) == fs_error::dont_exist) create_folder(current_path);
current_path += '/';
}
return true;
}
std::string
get_file_folder(std::string full_file_path) {
//! 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
if (last_slash_position == std::string::npos) return "";
return std::string(full_file_path, 0, last_slash_position);
}
std::string
home(void) {
struct passwd* passwdEnt = getpwuid(getuid());
return { passwdEnt->pw_dir };
}
bool
copy(const std::string& src, const std::string& dst) {
std::ifstream src_stream(src, std::ios::binary);
std::ofstream dst_stream(dst, std::ios::binary);
if (not src_stream.is_open()) return false;
dst_stream << src_stream.rdbuf();
return true;
}
} // fs
} // tool
} // etix
@@ -0,0 +1,39 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <launch_command.h>
namespace etix {
namespace cameradar {
//! Launches a command and checks for the return value
bool
launch_command(const std::string& cmd) {
int status = system(cmd.c_str());
if (status < 0) {
LOG_ERR_("Error: " + std::string(strerror(errno)) + "", "dispatcher");
return false;
} else {
if (WIFEXITED(status)) {
LOG_DEBUG_("Program returned normally, exit code " +
std::to_string(WEXITSTATUS(status)),
"dispatcher");
return true;
} else
LOG_WARN_("Program exited abnormaly.", "dispatcher");
}
return false;
}
}
}
+130
View File
@@ -0,0 +1,130 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fs.h> // fs::home
#include <opt_parse.h> // parsing opt
#include <dispatcher.h> // program loop
namespace cmrdr = etix::cameradar;
// Command line parsing
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("-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);
opt_parse.optional("-b", "Launch the bruteforce tool on all discovered devices", false);
opt_parse.optional("-t", "Generate thumbnails from detected cameras", false);
opt_parse.optional("-g", "Check if the stream can be opened with GStreamer", false);
opt_parse.optional("-v", "Display Cameradar's version", false);
opt_parse.optional("-h", "Display this help", false);
opt_parse.execute();
if (opt_parse.exist("-h")) {
opt_parse.print_help();
return std::make_pair(false, opt_parse);
} else if (opt_parse.exist("-v")) {
std::cout << "Cameradar 0.1" << std::endl;
return std::make_pair(false, opt_parse);
} else if (opt_parse.has_error()) {
std::cout << "Usage: ./cameradar [option]\n\toptions:\n" << std::endl;
opt_parse.print_help();
return std::make_pair(false, opt_parse);
}
return std::make_pair(true, opt_parse);
}
// Check if a folder exists, is readable and writable
bool
check_folder(const std::string& path) {
struct stat sb;
if ((stat(path.c_str(), &sb) == 0) && (S_ISDIR(sb.st_mode)) && (sb.st_mode & S_IRUSR) &&
(sb.st_mode & S_IWUSR)) {
LOG_INFO_("Folder " + path + " is available and has sufficient rights", "main");
return true;
}
LOG_ERR_("Folder " + path + " has insufficient rights, please check your configuration",
"main");
return false;
}
// Check if the storage path is available
bool
check_storage_path(const std::string& thumbnail_storage_path) {
LOG_INFO_("Checking if storage path exists and are usable", "main");
return (check_folder(thumbnail_storage_path));
}
int
main(int argc, char* argv[]) {
etix::tool::logger::get_instance("cameradar");
auto args = parse_cmdline(argc, argv);
if (not args.first) return EXIT_FAILURE;
// 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");
} else {
try {
int level = std::stoi(args.second["-l"]);
etix::tool::logger::get_instance("cameradar")
.set_level(static_cast<etix::tool::loglevel>(level));
} catch (...) {
LOG_ERR_("Invalid log level format, log level should be 1, 2, 4, 5 or 6", "main");
return EXIT_FAILURE;
}
}
// Try to load the configuration
auto conf = cmrdr::load(conf_path);
if (not conf.first) { return EXIT_FAILURE; }
LOG_INFO_("Configuration successfully loaded", "main");
// If one of the path is invalid, exit
auto paths_ok = check_storage_path(conf.second.thumbnail_storage_path);
if (not paths_ok) { return EXIT_FAILURE; }
// Here we should get the cache manager but for now we will juste
// make a dumb cache manager
auto plug = std::make_shared<etix::cameradar::cache_manager>(conf.second.cache_manager_path,
conf.second.cache_manager_name);
if (not plug->make_instance()) {
LOG_ERR_(std::string("Invalid cache manager "), "cameradar");
return false;
}
LOG_INFO_("Launching Cameradar, press CTRL+C to gracefully stop", "main");
etix::cameradar::dispatcher disp(conf.second, plug, args);
disp.run();
LOG_WARN_("See ya !", "cameradar");
return EXIT_SUCCESS;
}
+131
View File
@@ -0,0 +1,131 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "opt_parse.h"
#include <iostream>
namespace etix {
namespace tool {
opt_parse::opt_parse(int argc, char* argv[]) : argc(argc), argv(argv) {}
opt_parse::~opt_parse() {}
void
opt_parse::required(const std::string& name, const std::string& desc, bool need_arg) {
this->params.emplace(name, opt_param(true, need_arg, name, desc));
}
void
opt_parse::optional(const std::string& name, const std::string& desc, bool need_arg) {
this->params.emplace(name, opt_param(false, need_arg, name, desc));
}
bool
opt_parse::execute() {
int i = 1;
// if params are invalid
if (this->argc < 1 || not this->argv) { return false; }
while (i != this->argc) {
// there is less argument than argc
if (not this->argv[i]) { return false; }
auto params = this->params.find(std::string(this->argv[i]));
if (params != this->params.end()) {
this->params_cnt += 1;
(*params).second.is_passed = true;
if ((*params).second.need_arg == true && (i + 1) != this->argc) {
(*params).second.argument = this->argv[i + 1];
i += 1;
}
}
i += 1;
}
return true;
}
opt_parse::iterator
opt_parse::begin() const {
std::vector<std::pair<std::string, std::string>> p;
for (auto entry : this->params) {
p.push_back(std::make_pair(entry.second.name, entry.second.argument));
}
return iterator(p, 0);
}
opt_parse::iterator
opt_parse::end() const {
return iterator(std::vector<std::pair<std::string, std::string>>(), this->params_cnt);
}
void
opt_parse::print_usage() const {
std::cout << "Usage: " << this->argv[0];
for (auto entry : this->params) {
if (entry.second.required == true) {
if (entry.second.need_arg == true) { std::cout << " <arg>"; }
}
}
std::cout << std::endl;
}
void
opt_parse::print_help() const {
std::cout << "help: " << this->argv[0] << std::endl;
for (auto entry : this->params) {
std::cout << entry.second.name << " " << entry.second.desc << std::endl;
}
}
bool
opt_parse::has_error() const {
for (auto entry : this->params) {
// is the parameter required ?
// the parameter need arguement ?
if ((entry.second.required == true && entry.second.is_passed == false) ||
(entry.second.is_passed == true && entry.second.need_arg == true &&
entry.second.argument == "")) {
return true;
}
}
return false;
}
bool
opt_parse::exist(const std::string& opt) const {
auto params = this->params.find(opt);
if (params == this->params.end()) { return false; }
return (*params).second.is_passed;
}
std::string opt_parse::operator[](const std::string& opt) const {
std::string param("");
auto opt_param = this->params.find(opt);
if (opt_param != this->params.end()) { param = (*opt_param).second.argument; }
return param;
}
} // tool
} // etix
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rtsp_path.h>
#include <logger.h>
namespace etix {
namespace cameradar {
const std::string
make_path(const stream_model& model) {
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
model.address + ":" + std::to_string(model.port) + model.route);
LOG_DEBUG_(ret, "debug");
return ret;
}
}
}
@@ -0,0 +1,47 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <signal_handler.h>
namespace etix {
namespace cameradar {
event_handler signal_handler::handler;
signal_handler::signal_handler() {}
void
signal_handler::call_handler(int signum) {
handler.handle_signal(signum);
}
signal_handler&
signal_handler::instance(void) {
static signal_handler singleton;
struct sigaction sa;
sa.sa_handler = call_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, 0);
return singleton;
}
etix::cameradar::stop_priority
signal_handler::should_stop(void) const {
return handler.should_stop();
}
}
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stream_model.h>
namespace etix {
namespace cameradar {
Json::Value
deserialize(const stream_model& model) {
Json::Value ret;
ret["address"] = model.address;
ret["port"] = model.port;
ret["username"] = model.username;
ret["password"] = model.password;
ret["route"] = model.route;
ret["service_name"] = model.service_name;
ret["product"] = model.product;
ret["protocol"] = model.protocol;
ret["state"] = model.state;
ret["path_found"] = model.path_found;
ret["ids_found"] = model.ids_found;
ret["thumbnail_path"] = model.thumbnail_path;
return ret;
}
}
}
@@ -0,0 +1,122 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <tasks/brutelogs.h>
#include <cachemanager.h>
namespace etix {
namespace cameradar {
static const std::string no_ids_warning_ =
"The ids.json files' default paths didn't match with the discovered "
"cameras. Either "
"they have custom ids, or your ids.json file does not contain enough "
"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
bool
brutelogs::test_ids(const etix::cameradar::stream_model& stream,
const std::string& password,
const std::string& username) const {
bool found = false;
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, "bruteforce");
try {
if (curl_describe(path, true)) {
LOG_DEBUG_("[FOUND IDS] : " + path, "bruteforce");
found = true;
stream_model newstream{
stream.address, stream.port, username, password,
stream.route, stream.service_name, stream.product, stream.protocol,
stream.state, stream.path_found, true, stream.thumbnail_path
};
(*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,
stream.path_found, false, stream.thumbnail_path };
(*cache)->update_stream(newstream);
}
} catch (const std::runtime_error& e) {
LOG_DEBUG_("Ids already tested : " + std::string(e.what()), "bruteforce");
}
return found;
}
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;
}
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 {
LOG_INFO_(
"Beginning bruteforce of the usernames and passwords task, it may "
"take a while.",
"bruteforce");
std::vector<etix::cameradar::stream_model> streams = (*cache)->get_streams();
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)) {
LOG_INFO_(stream.address +
" : This camera's ids were already discovered in "
"the database. Skipping to "
"the next camera.",
"bruteforce");
++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;
}
}
}
}
}
if (!found) {
LOG_WARN_(no_ids_warning_, "bruteforce");
return false;
} else
LOG_INFO_("Found " + std::to_string(found) + " ids for " + std::to_string(streams.size()) +
" cameras",
"bruteforce");
return true;
}
}
}
@@ -0,0 +1,103 @@
// Copyright (C) 2016 Etix Labs - All Rights Reserved.
// All information contained herein is, and remains the property of Etix Labs
// and its suppliers,
// if any. The intellectual and technical concepts contained herein are
// proprietary to Etix Labs
// Dissemination of this information or reproduction of this material is
// strictly forbidden unless
// prior written permission is obtained from Etix Labs.
#include <tasks/brutepath.h>
namespace etix {
namespace cameradar {
static const std::string no_route_found_ =
"The url.json files' default paths didn't match with the discovered "
"cameras. Either "
"they have a custom path, or your url.json file does not contain enough "
"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
bool
brutepath::test_path(const stream_model& stream, const std::string& route) const {
bool found = false;
std::string path = stream.service_name + "://" + stream.username + ":" + stream.password + "@" +
stream.address + ":" + std::to_string(stream.port);
if (route.front() != '/') { path += "/"; }
path += route;
LOG_DEBUG_("Testing path : " + path, "brutepath");
try {
if (curl_describe(path, false)) {
// insert in DB and go to the next port, print a cool message
found = true;
LOG_INFO_("Discovered a valid path : [" + path + "]", "brutepath");
stream_model newstream{
stream.address, stream.port, stream.username, stream.password, route,
stream.service_name, stream.product, stream.protocol, stream.state, true,
stream.ids_found, stream.thumbnail_path
};
(*cache)->update_stream(newstream);
} else {
stream_model newstream{
stream.address, stream.port, stream.username, stream.password, route,
stream.service_name, stream.product, stream.protocol, stream.state, false,
stream.ids_found, stream.thumbnail_path
};
(*cache)->update_stream(newstream);
}
} catch (const std::runtime_error& e) { LOG_INFO_(e.what(), "brutepath"); }
return found;
}
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)
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::run() const {
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;
for (const auto& stream : streams) {
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
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.",
"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;
}
}
}
}
if (!found) {
LOG_WARN_(no_route_found_, "brutepath");
} else
LOG_INFO_("Found " + std::to_string(found) + " routes for " +
std::to_string(streams.size()) + " cameras",
"brutepath");
return true;
}
}
}
@@ -0,0 +1,59 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <tasks/mapping.h>
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.
bool
nmap_is_ok() {
return (launch_command("test `dpkg -l | cut -c 5-9 | grep nmap` = nmap")
// && launch_command("test `nmap --version | cut -c 14-18 | head -n2 | tail -n1` = 6.47")
&& launch_command("mkdir -p 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
bool
mapping::run() const {
if (nmap_is_ok()) {
std::string subnets = this->conf.subnets;
std::replace(subnets.begin(), subnets.end(), ',', ' ');
LOG_INFO_("Nmap 6.0 or greater found", "mapping");
LOG_INFO_("Beginning mapping task. This may take a while.", "mapping");
std::string cmd =
"nmap -T4 -A " + subnets + " -p " + this->conf.ports + " -oX " + nmap_output;
bool ret = launch_command(cmd);
if (ret)
LOG_INFO_("Nmap XML output successfully generated in file: " + nmap_output, "mapping");
else
LOG_ERR_("Nmap command failed", "mapping");
return ret;
} else {
LOG_ERR_("Nmap 6.0 or greater is required to launch Cameradar", "mapping");
return false;
}
}
}
}
+122
View File
@@ -0,0 +1,122 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <tasks/parsing.h>
namespace etix {
namespace cameradar {
static const std::string no_hosts_found_ =
"No hosts were discovered on your network. Please check your internet "
"connexion "
"and verify that the subnetworks you specified in your configuration file "
"were "
"accessible";
//! Avoids segfaults on unknown xml structure
std::string
xml_safe_get(const TiXmlElement* elem, const std::string& attr) {
if (elem == nullptr) return "Closed";
if (elem->Attribute(attr.c_str()) != nullptr) return std::string(elem->Attribute(attr.c_str()));
return "Closed";
}
//! 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();
stream_model stream;
for (TiXmlElement* xml_stream = xml_streams->FirstChild("port")->ToElement(); xml_stream;
xml_stream = xml_stream->NextSiblingElement("port")) {
stream.address = xml_safe_get(xml_host->FirstChild("address")->ToElement(), "addr");
stream.protocol = xml_safe_get(xml_stream, "protocol");
stream.port = static_cast<unsigned short>(std::stoi(xml_safe_get(xml_stream, "portid")));
TiXmlElement* state = xml_stream->FirstChild("state")->ToElement();
stream.state = xml_safe_get(state, "state");
TiXmlElement* service;
if (state->NextSibling("service") &&
(service = state->NextSibling("service")->ToElement())) {
stream.service_name = xml_safe_get(service, "name");
stream.product = xml_safe_get(service, "product");
} else {
stream.service_name = "Closed";
stream.product = "Closed";
}
data.push_back(stream);
}
}
//! 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;
for (const auto& stream : data) {
if (!stream.service_name.compare("rtsp") && !stream.state.compare("open")) {
try {
LOG_INFO_("Generated JSON Result : " + deserialize(stream).toStyledString(),
"print");
added++;
} catch (const std::runtime_error& e) {
LOG_WARN_("Port already scanned : " + std::string(e.what()), "parsing");
added++;
}
}
}
if (!added) {
LOG_WARN_(
"Mapping unsuccessful, no rtsp streams were discovered. You "
"should try other "
"subnetworks",
"parsing");
return false;
}
LOG_INFO_("Mapping successfuly ended, " + std::to_string(added) +
" RTSP streams were discovered.",
"parsing");
(*cache)->set_streams(data);
return true;
}
//! 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;
try {
TiXmlDocument doc(nmap_output.c_str());
doc.LoadFile();
TiXmlHandle docHandle(&doc);
TiXmlElement* nmaprun = docHandle.FirstChild("nmaprun").ToElement();
TiXmlNode* xml_node = nmaprun->FirstChild("host");
if (xml_node == NULL) return false;
TiXmlElement* xml_host;
if ((xml_host = xml_node->ToElement()) && xml_host->Attribute("endtime"))
for (xml_host = xml_node->ToElement(); xml_host;
xml_host = xml_host->NextSiblingElement("host")) {
parse_camera(xml_host, data);
}
else
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) {
LOG_ERR_("Error during parsing. brutepath aborted : " + std::string(e.what()), "parsing");
return false;
}
}
}
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <tasks/print.h>
namespace etix {
namespace cameradar {
//! 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();
std::ofstream file;
bool first = true;
file.open("result.json");
file << "[\n";
for (const auto& stream : results) {
LOG_INFO_("Found a valid RTSP Stream and generated a thumbnail at : " +
stream.thumbnail_path,
"print");
if (first)
first = false;
else
file << ",";
file << deserialize(stream).toStyledString();
LOG_INFO_("Generated JSON Result : " + deserialize(stream).toStyledString(), "print");
}
file << "\n]";
file.close();
return true;
}
}
}
@@ -0,0 +1,66 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <tasks/stream_check.h>
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
bool
stream_check::run() const {
GstElement* pipeline;
GstElement* elem;
gst_init(nullptr, nullptr);
std::vector<stream_model> streams = (*cache)->get_valid_streams();
for (const auto& stream : streams) {
GError* error = NULL;
pipeline =
gst_parse_launch("rtspsrc name=source ! rtph264depay ! h264parse ! fakesink", &error);
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");
g_object_set(G_OBJECT(elem), "location", stream.address, "latency", 20, NULL);
if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
LOG_ERR_(
"This stream is unaccessible with GStreamer, there must be a "
"configuration issue",
"stream_check");
gst_object_unref(pipeline);
stream_model invalidstream{
stream.address, stream.port, stream.username, stream.password,
stream.route, stream.service_name, stream.product, stream.protocol,
"invalid stream", stream.path_found, stream.ids_found, stream.thumbnail_path
};
(*cache)->update_stream(invalidstream);
return false;
}
LOG_INFO_("[" + stream.address + "] Set pipeline to playing", "stream_check");
}
}
LOG_INFO_("All streams could be accessed with GStreamer", "stream_check");
return true;
}
}
}
@@ -0,0 +1,97 @@
// Copyright 2016 Etix Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <tasks/thumbnail.h>
namespace etix {
namespace cameradar {
std::string
remove_trailing_backslash(std::string s) {
while (s.back() == '/') { s.pop_back(); }
return s;
}
// Tranforms the path into a path for the thumbnail
// Example :
// rtsp://username:password@172.16.100.13/live.sdp
// will become /storage/path/172.16.100.13/1345425533.jpg
std::string
thumbnail::build_output_file_path(const std::string& path) const {
auto ss = std::stringstream{};
ss << remove_trailing_backslash(this->conf.thumbnail_storage_path);
ss << "/";
ss << path;
ss << "/";
ss << std::to_string(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
ss << ".jpg";
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::run() const {
std::vector<stream_model> streams = (*cache)->get_valid_streams();
LOG_INFO_("Started thumbnail generation, it may take a while", "thumbnail");
for (const auto& stream : streams) {
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"); }
}
}
LOG_INFO_("All thumbnails have been successfully generated in " +
this->conf.thumbnail_storage_path,
"thumbnail_generation");
return true;
}
}
}
+94
View File
@@ -0,0 +1,94 @@
/*
www.sourceforge.net/projects/tinyxml
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef TIXML_USE_STL
#include "tinystr.h"
// Error value for find primitive
const TiXmlString::size_type TiXmlString::npos = static_cast<TiXmlString::size_type>(-1);
// Null rep.
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
void
TiXmlString::reserve(size_type cap) {
if (cap > capacity()) {
TiXmlString tmp;
tmp.init(length(), cap);
memcpy(tmp.start(), data(), length());
swap(tmp);
}
}
TiXmlString&
TiXmlString::assign(const char* str, size_type len) {
size_type cap = capacity();
if (len > cap || cap > 3 * (len + 8)) {
TiXmlString tmp;
tmp.init(len);
memcpy(tmp.start(), str, len);
swap(tmp);
} else {
memmove(start(), str, len);
set_size(len);
}
return *this;
}
TiXmlString&
TiXmlString::append(const char* str, size_type len) {
size_type newsize = length() + len;
if (newsize > capacity()) { reserve(newsize + capacity()); }
memmove(finish(), str, len);
set_size(newsize);
return *this;
}
TiXmlString operator+(const TiXmlString& a, const TiXmlString& b) {
TiXmlString tmp;
tmp.reserve(a.length() + b.length());
tmp += a;
tmp += b;
return tmp;
}
TiXmlString operator+(const TiXmlString& a, const char* b) {
TiXmlString tmp;
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>(strlen(b));
tmp.reserve(a.length() + b_len);
tmp += a;
tmp.append(b, b_len);
return tmp;
}
TiXmlString operator+(const char* a, const TiXmlString& b) {
TiXmlString tmp;
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>(strlen(a));
tmp.reserve(a_len + b.length());
tmp.append(a, a_len);
tmp += b;
return tmp;
}
#endif // TIXML_USE_STL
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
/*
www.sourceforge.net/projects/tinyxml
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "tinyxml.h"
// The goal of the seperate error file is to make the first
// step towards localization. tinyxml (currently) only supports
// english error messages, but the could now be translated.
//
// It also cleans up the code a bit.
//
const char* TiXmlBase::errorString[TiXmlBase::TIXML_ERROR_STRING_COUNT] = {
"No error",
"Error",
"Failed to open file",
"Error parsing Element.",
"Failed to read Element name",
"Error reading Element value.",
"Error reading Attributes.",
"Error: empty tag.",
"Error reading end tag.",
"Error parsing Unknown.",
"Error parsing Comment.",
"Error parsing Declaration.",
"Error document empty.",
"Error null (0) or unexpected EOF found in input stream.",
"Error parsing CDATA.",
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
};
File diff suppressed because it is too large Load Diff