Refactored (#31)

* Build system changed to CMake
* Converted tabs to spaces
* Removed all usages of boost::log
* Replaced boost::thread with std::thread
* Update copyright year
* Added a tiny bit of unit tests
* Added a simple message queue
* Added basic independent logging
* Added CONTRIBUTING
This commit is contained in:
Naim A 2017-09-27 04:33:21 +03:00 committed by Naim A
parent 7193f945a9
commit f34fcdbd04
No known key found for this signature in database
GPG key ID: FD7948915D9EF8B9
29 changed files with 3243 additions and 3091 deletions

44
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,44 @@
# Contributing
Thank you for looking into [UDPT](https://github.com/naim94a/udpt)!
This document lists various ways to contribute to UDPT. If you can, please do.
## Bug Reports
Bug reports are always welcome, they help us maintain a quality product.
Before submitting a bug report, please first check that the issue does not already exist and that the effected version
is the most recent one.
* If a similar (or same) issue already exists, please upvote, comment, elaborate on the existing issue.
* If you found a new issue, please attach the used configuration and how to cause the bug to appear.
* Core dumps are always welcome
Bugs should be reported at [UDPT's issue tracker](https://github.com/naim94a/udpt) on GitHub.
## Suggesting new features
New features are welcome, it doesn't mean they will enter immediately.
Suggestions should be filed as issues along with bug reports, just add the "enhancement" label to the created issue.
## Donations
Please show us your appreciation by donating BitCoin at bitcoin:1KMeZvcgnmWdHitu51yEFWBNcSTXL1eBk3.
![bitcoin:1KMeZvcgnmWdHitu51yEFWBNcSTXL1eBk3](bitcoin-qr.png)
## Pull requests
Before submitting a pull request, please check that the changes don't effect other platforms or have any unwanted
side-effects.
Pull Requests should address open issue(s), where the project owner and contributors can discuss how the issue should be
addressed.
Pull requests that add new features should be first mentioned in issues in order to verify that the project owner will
allow such changes to the project.
Code in pull requests should meet the following requirements:
* Class names should be CamelCase, and the first letter should be capital.
* Functions should be camelCase, and the first letter should be lower-case.
* Class members should be prefixed with `m_`, for example: `bool m_isRunning`
* All variables, classes, functions and class members should have an indicative name.
* Adding external libraries is allowed, but frowned upon

BIN
.github/bitcoin-qr.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -1,15 +1,24 @@
project(udpt) project(udpt)
cmake_minimum_required(VERSION 3.6) cmake_minimum_required(VERSION 3.2)
enable_testing()
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-DBOOST_LOG_DYN_LINK)
file(GLOB src_files "src/*.c" file(GLOB src_files "src/*.c"
"src/*.cpp" "src/*.cpp"
"src/db/*.cpp" "src/db/*.cpp"
"src/http/*.cpp") "src/http/*.cpp")
LIST(APPEND LIBS "pthread" "sqlite3" "boost_program_options" "boost_system")
add_executable(udpt ${src_files}) add_executable(udpt ${src_files})
target_link_libraries(udpt pthread sqlite3 boost_log boost_program_options boost_thread boost_system) target_link_libraries(udpt ${LIBS})
add_executable(udpt_tests tests/main.cpp ${src_files})
target_compile_definitions(udpt_tests PRIVATE TEST=1)
target_link_libraries(udpt_tests gtest ${LIBS})
add_test(NAME udpt_tests COMMAND udpt_tests)

64
src/MessageQueue.hpp Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <queue>
#include <mutex>
namespace UDPT
{
namespace Utils {
template<class T>
class MessageQueue {
public:
MessageQueue() {}
virtual ~MessageQueue() {}
bool IsEmpty() const {
return m_queue.empty();
}
T Pop() {
m_queueMutex.lock();
T val = m_queue.front();
m_queue.pop();
m_queueMutex.unlock();
return val;
}
void Push(T obj) {
m_queueMutex.lock();
m_queue.push(obj);
m_queueMutex.unlock();
}
size_t Count() const {
return m_queue.size();
}
private:
std::queue<T> m_queue;
std::mutex m_queueMutex;
};
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -21,100 +21,100 @@
namespace UDPT namespace UDPT
{ {
namespace Data namespace Data
{ {
DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf) DatabaseDriver::DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic) : m_conf(conf)
{ {
this->is_dynamic = isDynamic; this->is_dynamic = isDynamic;
} }
bool DatabaseDriver::addTorrent(uint8_t hash [20]) bool DatabaseDriver::addTorrent(uint8_t hash [20])
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::removeTorrent(uint8_t hash[20]) bool DatabaseDriver::removeTorrent(uint8_t hash[20])
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::isDynamic() bool DatabaseDriver::isDynamic()
{ {
return this->is_dynamic; return this->is_dynamic;
} }
bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port) bool DatabaseDriver::genConnectionId(uint64_t *cid, uint32_t ip, uint16_t port)
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port) bool DatabaseDriver::verifyConnectionId(uint64_t cid, uint32_t ip, uint16_t port)
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], bool DatabaseDriver::updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port, uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded, int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event) enum TrackerEvents event)
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port) bool DatabaseDriver::removePeer (uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port)
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::getTorrentInfo (TorrentEntry *e) bool DatabaseDriver::getTorrentInfo (TorrentEntry *e)
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe) bool DatabaseDriver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
void DatabaseDriver::cleanup() void DatabaseDriver::cleanup()
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20]) bool DatabaseDriver::isTorrentAllowed(uint8_t info_hash[20])
{ {
throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED); throw DatabaseException (DatabaseException::E_NOT_IMPLEMENTED);
} }
DatabaseDriver::~DatabaseDriver() DatabaseDriver::~DatabaseDriver()
{ {
} }
/*-- Exceptions --*/ /*-- Exceptions --*/
static const char *EMessages[] = { static const char *EMessages[] = {
"Unknown Error", "Unknown Error",
"Not Implemented", "Not Implemented",
"Failed to connect to database" "Failed to connect to database"
}; };
DatabaseException::DatabaseException() DatabaseException::DatabaseException()
{ {
this->errorNum = E_UNKNOWN; this->errorNum = E_UNKNOWN;
} }
DatabaseException::DatabaseException(enum EType e) DatabaseException::DatabaseException(enum EType e)
{ {
this->errorNum = e; this->errorNum = e;
} }
enum DatabaseException::EType DatabaseException::getErrorType() enum DatabaseException::EType DatabaseException::getErrorType()
{ {
return this->errorNum; return this->errorNum;
} }
const char* DatabaseException::getErrorMessage() const char* DatabaseException::getErrorMessage()
{ {
return EMessages[this->errorNum]; return EMessages[this->errorNum];
} }
}; };
}; };

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -21,154 +21,152 @@
#define DATABASE_HPP_ #define DATABASE_HPP_
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
namespace UDPT namespace UDPT
{ {
namespace Data namespace Data
{ {
class DatabaseException class DatabaseException
{ {
public: public:
enum EType { enum EType {
E_UNKNOWN = 0, // Unknown error E_UNKNOWN = 0, // Unknown error
E_NOT_IMPLEMENTED = 1, // not implemented E_NOT_IMPLEMENTED = 1, // not implemented
E_CONNECTION_FAILURE = 2 E_CONNECTION_FAILURE = 2
}; };
DatabaseException(); DatabaseException();
DatabaseException(EType); DatabaseException(EType);
EType getErrorType(); EType getErrorType();
const char* getErrorMessage(); const char* getErrorMessage();
private: private:
EType errorNum; EType errorNum;
}; };
class DatabaseDriver class DatabaseDriver
{ {
public: public:
typedef struct { typedef struct {
uint8_t *info_hash; uint8_t *info_hash;
int32_t seeders; int32_t seeders;
int32_t leechers; int32_t leechers;
int32_t completed; int32_t completed;
} TorrentEntry; } TorrentEntry;
typedef struct { typedef struct {
uint32_t ip; uint32_t ip;
uint16_t port; uint16_t port;
} PeerEntry; } PeerEntry;
enum TrackerEvents { enum TrackerEvents {
EVENT_UNSPEC = 0, EVENT_UNSPEC = 0,
EVENT_COMPLETE = 1, EVENT_COMPLETE = 1,
EVENT_START = 2, EVENT_START = 2,
EVENT_STOP = 3 EVENT_STOP = 3
}; };
/** /**
* Opens the DB's connection * Opens the DB's connection
* @param dClass Settings class ('database' class). * @param dClass Settings class ('database' class).
*/ */
DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic = false); DatabaseDriver(const boost::program_options::variables_map& conf, bool isDynamic = false);
/** /**
* Adds a torrent to the Database. automatically done if in dynamic mode. * Adds a torrent to the Database. automatically done if in dynamic mode.
* @param hash The info_hash of the torrent. * @param hash The info_hash of the torrent.
* @return true on success. false on failure. * @return true on success. false on failure.
*/ */
virtual bool addTorrent(uint8_t hash[20]); virtual bool addTorrent(uint8_t hash[20]);
/** /**
* Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup. * Removes a torrent from the database. should be used only for non-dynamic trackers or by cleanup.
* @param hash The info_hash to drop. * @param hash The info_hash to drop.
* @return true if torrent's database was dropped or no longer exists. otherwise false (shouldn't happen - critical) * @return true if torrent's database was dropped or no longer exists. otherwise false (shouldn't happen - critical)
*/ */
virtual bool removeTorrent(uint8_t hash[20]); virtual bool removeTorrent(uint8_t hash[20]);
/** /**
* Checks if the Database is acting as a dynamic tracker DB. * Checks if the Database is acting as a dynamic tracker DB.
* @return true if dynamic. otherwise false. * @return true if dynamic. otherwise false.
*/ */
bool isDynamic(); bool isDynamic();
/** /**
* Checks if the torrent can be used in the tracker. * Checks if the torrent can be used in the tracker.
* @param info_hash The torrent's info_hash. * @param info_hash The torrent's info_hash.
* @return true if allowed. otherwise false. * @return true if allowed. otherwise false.
*/ */
virtual bool isTorrentAllowed(uint8_t info_hash [20]); virtual bool isTorrentAllowed(uint8_t info_hash [20]);
/** /**
* Generate a Connection ID for the peer. * Generate a Connection ID for the peer.
* @param connectionId (Output) the generated connection ID. * @param connectionId (Output) the generated connection ID.
* @param ip The peer's IP (requesting peer. not remote) * @param ip The peer's IP (requesting peer. not remote)
* @param port The peer's IP (remote port if tracker accepts) * @param port The peer's IP (remote port if tracker accepts)
* @return * @return
*/ */
virtual bool genConnectionId(uint64_t *connectionId, uint32_t ip, uint16_t port); virtual bool genConnectionId(uint64_t *connectionId, uint32_t ip, uint16_t port);
virtual bool verifyConnectionId(uint64_t connectionId, uint32_t ip, uint16_t port); virtual bool verifyConnectionId(uint64_t connectionId, uint32_t ip, uint16_t port);
/** /**
* Updates/Adds a peer to/in the database. * Updates/Adds a peer to/in the database.
* @param peer_id the peer's peer_id * @param peer_id the peer's peer_id
* @param info_hash the torrent info_hash * @param info_hash the torrent info_hash
* @param ip IP of peer (remote ip if tracker accepts) * @param ip IP of peer (remote ip if tracker accepts)
* @param port TCP port of peer (remote port if tracker accepts) * @param port TCP port of peer (remote port if tracker accepts)
* @param downloaded total Bytes downloaded * @param downloaded total Bytes downloaded
* @param left total bytes left * @param left total bytes left
* @param uploaded total bytes uploaded * @param uploaded total bytes uploaded
* @return true on success, false on failure. * @return true on success, false on failure.
*/ */
virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], virtual bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20],
uint32_t ip, uint16_t port, uint32_t ip, uint16_t port,
int64_t downloaded, int64_t left, int64_t uploaded, int64_t downloaded, int64_t left, int64_t uploaded,
enum TrackerEvents event); enum TrackerEvents event);
/** /**
* Remove a peer from a torrent (if stop action occurred, or if peer is inactive in cleanup) * Remove a peer from a torrent (if stop action occurred, or if peer is inactive in cleanup)
* @param peer_id The peer's peer_id * @param peer_id The peer's peer_id
* @param info_hash Torrent's info_hash * @param info_hash Torrent's info_hash
* @param ip The IP of the peer (remote IP if tracker accepts) * @param ip The IP of the peer (remote IP if tracker accepts)
* @param port The TCP port (remote port if tracker accepts) * @param port The TCP port (remote port if tracker accepts)
* @return true on success. false on failure (shouldn't happen - critical) * @return true on success. false on failure (shouldn't happen - critical)
*/ */
virtual bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port); virtual bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
/** /**
* Gets stats on a torrent * Gets stats on a torrent
* @param e TorrentEntry, only this info_hash has to be set * @param e TorrentEntry, only this info_hash has to be set
* @return true on success, false on failure. * @return true on success, false on failure.
*/ */
virtual bool getTorrentInfo(TorrentEntry *e); virtual bool getTorrentInfo(TorrentEntry *e);
/** /**
* Gets a list of peers from the database. * Gets a list of peers from the database.
* @param info_hash The torrent's info_hash * @param info_hash The torrent's info_hash
* @param max_count The maximum amount of peers to load from the database. The amount of loaded peers is returned through this variable. * @param max_count The maximum amount of peers to load from the database. The amount of loaded peers is returned through this variable.
* @param pe The list of peers. Must be pre-allocated to the size of max_count. * @param pe The list of peers. Must be pre-allocated to the size of max_count.
* @return true on success, otherwise false (shouldn't happen). * @return true on success, otherwise false (shouldn't happen).
*/ */
virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe); virtual bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
/** /**
* Cleanup the database. * Cleanup the database.
* Other actions may be locked when using this depending on the driver. * Other actions may be locked when using this depending on the driver.
*/ */
virtual void cleanup(); virtual void cleanup();
/** /**
* Closes the connections, and releases all other resources. * Closes the connections, and releases all other resources.
*/ */
virtual ~DatabaseDriver(); virtual ~DatabaseDriver();
protected: protected:
const boost::program_options::variables_map& m_conf; const boost::program_options::variables_map& m_conf;
private: private:
bool is_dynamic; bool is_dynamic;
}; };
}; };
}; };

View file

@ -1,431 +1,435 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "driver_sqlite.hpp" #include "driver_sqlite.hpp"
#include "../tools.h" #include "../tools.h"
#include <ctime> #include <ctime>
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>
#include <cstring> // memcpy #include <cstring> // memcpy
#include "../multiplatform.h" #include "../multiplatform.h"
#include "../logging.hpp"
using namespace std;
using namespace std;
namespace UDPT
{ namespace UDPT
namespace Data {
{ namespace Data
static const char hexadecimal[] = "0123456789abcdef"; {
static const char hexadecimal[] = "0123456789abcdef";
static char* _to_hex_str (const uint8_t *hash, char *data)
{ static char* _to_hex_str (const uint8_t *hash, char *data)
int i; {
for (i = 0;i < 20;i++) int i;
{ for (i = 0;i < 20;i++)
data[i * 2] = hexadecimal[hash[i] / 16]; {
data[i * 2 + 1] = hexadecimal[hash[i] % 16]; data[i * 2] = hexadecimal[hash[i] / 16];
} data[i * 2 + 1] = hexadecimal[hash[i] % 16];
data[40] = '\0'; }
return data; data[40] = '\0';
} return data;
}
static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
{ static uint8_t* _hash_to_bin (const char *hash, uint8_t *data)
for (int i = 0;i < 20;i++) {
{ for (int i = 0;i < 20;i++)
data [i] = 0; {
char a = hash[i * 2]; data [i] = 0;
char b = hash[i * 2 + 1]; char a = hash[i * 2];
char b = hash[i * 2 + 1];
assert ( (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9') );
assert ( (b >= 'a' && b <= 'f') || (b >= '0' && b <= '9') ); assert ( (a >= 'a' && a <= 'f') || (a >= '0' && a <= '9') );
assert ( (b >= 'a' && b <= 'f') || (b >= '0' && b <= '9') );
data[i] = ( (a >= '0' && a <= 'f') ? (a - '0') : (a - 'f' + 10) );
data[i] <<= 4; data[i] = ( (a >= '0' && a <= 'f') ? (a - '0') : (a - 'f' + 10) );
data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) ); data[i] <<= 4;
} data[i] = ( (b >= '0' && b <= 'f') ? (b - '0') : (b - 'f' + 10) );
}
return data;
} return data;
}
SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn), m_logger(boost::log::keywords::channel="SQLite3")
{ SQLite3Driver::SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn) : DatabaseDriver(conf, isDyn)
int r; {
bool doSetup; int r;
bool doSetup;
fstream fCheck;
string filename = m_conf["db.param"].as<std::string>(); fstream fCheck;
string filename = m_conf["db.param"].as<std::string>();
fCheck.open(filename.c_str(), ios::binary | ios::in);
if (fCheck.is_open()) fCheck.open(filename.c_str(), ios::binary | ios::in);
{ if (fCheck.is_open())
doSetup = false; {
fCheck.close(); doSetup = false;
} fCheck.close();
else }
doSetup = true; else
doSetup = true;
r = sqlite3_open(filename.c_str(), &this->db);
if (r != SQLITE_OK) r = sqlite3_open(filename.c_str(), &this->db);
{ if (r != SQLITE_OK)
sqlite3_close(this->db); {
throw DatabaseException (DatabaseException::E_CONNECTION_FAILURE); LOG_FATAL("db-sqlite", "Failed to connect DB. sqlite returned " << r);
} sqlite3_close(this->db);
throw DatabaseException (DatabaseException::E_CONNECTION_FAILURE);
if (doSetup) }
this->doSetup();
} if (doSetup)
this->doSetup();
void SQLite3Driver::doSetup() }
{
char *eMsg = NULL; void SQLite3Driver::doSetup()
// for quicker stats. {
sqlite3_exec(this->db, "CREATE TABLE stats (" char *eMsg = NULL;
"info_hash blob(20) UNIQUE," LOG_INFO("db-sqlite", "Setting up database...");
"completed INTEGER DEFAULT 0," // for quicker stats.
"leechers INTEGER DEFAULT 0," sqlite3_exec(this->db, "CREATE TABLE stats ("
"seeders INTEGER DEFAULT 0," "info_hash blob(20) UNIQUE,"
"last_mod INTEGER DEFAULT 0" "completed INTEGER DEFAULT 0,"
")", NULL, NULL, &eMsg); "leechers INTEGER DEFAULT 0,"
"seeders INTEGER DEFAULT 0,"
sqlite3_exec(this->db, "CREATE TABLE torrents (" "last_mod INTEGER DEFAULT 0"
"info_hash blob(20) UNIQUE," ")", NULL, NULL, &eMsg);
"created INTEGER"
")", NULL, NULL, &eMsg); sqlite3_exec(this->db, "CREATE TABLE torrents ("
} "info_hash blob(20) UNIQUE,"
"created INTEGER"
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e) ")", NULL, NULL, &eMsg);
{ }
bool gotInfo = false;
bool SQLite3Driver::getTorrentInfo(TorrentEntry *e)
const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?"; {
sqlite3_stmt *stmt; bool gotInfo = false;
e->seeders = 0; const char sql[] = "SELECT seeders,leechers,completed FROM 'stats' WHERE info_hash=?";
e->leechers = 0; sqlite3_stmt *stmt;
e->completed = 0;
e->seeders = 0;
e->leechers = 0;
sqlite3_prepare (this->db, sql, -1, &stmt, NULL); e->completed = 0;
sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW) sqlite3_prepare (this->db, sql, -1, &stmt, NULL);
{ sqlite3_bind_blob (stmt, 1, (void*)e->info_hash, 20, NULL);
e->seeders = sqlite3_column_int (stmt, 0);
e->leechers = sqlite3_column_int (stmt, 1); if (sqlite3_step(stmt) == SQLITE_ROW)
e->completed = sqlite3_column_int (stmt, 2); {
e->seeders = sqlite3_column_int (stmt, 0);
gotInfo = true; e->leechers = sqlite3_column_int (stmt, 1);
} e->completed = sqlite3_column_int (stmt, 2);
sqlite3_finalize (stmt); gotInfo = true;
}
return gotInfo;
} sqlite3_finalize (stmt);
bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe) return gotInfo;
{ }
string sql;
char hash [50]; bool SQLite3Driver::getPeers (uint8_t info_hash [20], int *max_count, PeerEntry *pe)
sqlite3_stmt *stmt; {
int r, i; string sql;
char hash [50];
to_hex_str(info_hash, hash); sqlite3_stmt *stmt;
int r, i;
sql = "SELECT ip,port FROM 't";
sql += hash; to_hex_str(info_hash, hash);
sql += "' LIMIT ?";
sql = "SELECT ip,port FROM 't";
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL); sql += hash;
sqlite3_bind_int(stmt, 1, *max_count); sql += "' LIMIT ?";
i = 0; sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
while (*max_count > i) sqlite3_bind_int(stmt, 1, *max_count);
{
r = sqlite3_step(stmt); i = 0;
if (r == SQLITE_ROW) while (*max_count > i)
{ {
const char *ip = (const char*)sqlite3_column_blob (stmt, 0); r = sqlite3_step(stmt);
const char *port = (const char*)sqlite3_column_blob (stmt, 1); if (r == SQLITE_ROW)
{
memcpy(&pe[i].ip, ip, 4); const char *ip = (const char*)sqlite3_column_blob (stmt, 0);
memcpy(&pe[i].port, port, 2); const char *port = (const char*)sqlite3_column_blob (stmt, 1);
i++; memcpy(&pe[i].ip, ip, 4);
} memcpy(&pe[i].port, port, 2);
else
{ i++;
break; }
} else
} {
break;
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Retrieved " << i << " peers"; }
}
sqlite3_finalize(stmt);
sqlite3_finalize(stmt);
*max_count = i;
*max_count = i;
return true;
} return true;
}
bool SQLite3Driver::updatePeer(uint8_t peer_id[20], uint8_t info_hash[20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event)
{ bool SQLite3Driver::updatePeer(uint8_t peer_id[20], uint8_t info_hash[20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event)
char xHash [50]; // we just need 40 + \0 = 41. {
sqlite3_stmt *stmt; char xHash [50]; // we just need 40 + \0 = 41.
string sql; sqlite3_stmt *stmt;
int r; string sql;
int r;
char *hash = xHash;
to_hex_str(info_hash, hash); char *hash = xHash;
to_hex_str(info_hash, hash);
addTorrent (info_hash);
addTorrent (info_hash);
sql = "REPLACE INTO 't";
sql += hash; sql = "REPLACE INTO 't";
sql += "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)"; sql += hash;
sql += "' (peer_id,ip,port,uploaded,downloaded,left,last_seen) VALUES (?,?,?,?,?,?,?)";
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_prepare(this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_blob(stmt, 1, (void*)peer_id, 20, NULL);
sqlite3_bind_blob(stmt, 2, (void*)&ip, 4, NULL); sqlite3_bind_blob(stmt, 1, (void*)peer_id, 20, NULL);
sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL); sqlite3_bind_blob(stmt, 2, (void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL); sqlite3_bind_blob(stmt, 3, (void*)&port, 2, NULL);
sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL); sqlite3_bind_blob(stmt, 4, (void*)&uploaded, 8, NULL);
sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL); sqlite3_bind_blob(stmt, 5, (void*)&downloaded, 8, NULL);
sqlite3_bind_int(stmt, 7, time(NULL)); sqlite3_bind_blob(stmt, 6, (void*)&left, 8, NULL);
sqlite3_bind_int(stmt, 7, time(NULL));
r = sqlite3_step(stmt);
sqlite3_finalize(stmt); r = sqlite3_step(stmt);
sqlite3_finalize(stmt);
sql = "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)";
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL); sql = "REPLACE INTO stats (info_hash,last_mod) VALUES (?,?)";
sqlite3_bind_blob (stmt, 1, hash, 20, NULL); sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
sqlite3_bind_int (stmt, 2, time(NULL)); sqlite3_bind_blob (stmt, 1, hash, 20, NULL);
sqlite3_step (stmt); sqlite3_bind_int (stmt, 2, time(NULL));
sqlite3_finalize (stmt); sqlite3_step (stmt);
sqlite3_finalize (stmt);
return r;
} return r;
}
bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
{ bool SQLite3Driver::addTorrent (uint8_t info_hash[20])
char xHash [41]; {
char *err_msg; char xHash [41];
int r; char *err_msg;
int r;
_to_hex_str(info_hash, xHash);
_to_hex_str(info_hash, xHash);
sqlite3_stmt *stmt;
sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL); sqlite3_stmt *stmt;
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL); sqlite3_prepare(this->db, "INSERT INTO torrents (info_hash,created) VALUES (?,?)", -1, &stmt, NULL);
sqlite3_bind_int(stmt, 2, time(NULL)); sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_step(stmt); sqlite3_bind_int(stmt, 2, time(NULL));
sqlite3_finalize(stmt); sqlite3_step(stmt);
sqlite3_finalize(stmt);
string sql = "CREATE TABLE IF NOT EXISTS 't";
sql += xHash; string sql = "CREATE TABLE IF NOT EXISTS 't";
sql += "' ("; sql += xHash;
sql += "peer_id blob(20)," sql += "' (";
"ip blob(4)," sql += "peer_id blob(20),"
"port blob(2)," "ip blob(4),"
"uploaded blob(8)," // uint64 "port blob(2),"
"downloaded blob(8)," "uploaded blob(8)," // uint64
"left blob(8)," "downloaded blob(8),"
"last_seen INT DEFAULT 0"; "left blob(8),"
"last_seen INT DEFAULT 0";
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
sql += ", CONSTRAINT c1 UNIQUE (ip,port) ON CONFLICT REPLACE)";
// create table.
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg); // create table.
r = sqlite3_exec(this->db, sql.c_str(), NULL, NULL, &err_msg);
if (SQLITE_OK == r)
{ if (SQLITE_OK == r)
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Added torrent"; {
return true; return true;
} }
else else
{ {
BOOST_LOG_SEV(m_logger, boost::log::trivial::error) << "Failed to add torrent: SQLite3 error code = " << r; return false;
return false; }
} }
}
bool SQLite3Driver::isTorrentAllowed(uint8_t *info_hash)
bool SQLite3Driver::isTorrentAllowed(uint8_t *info_hash) {
{ if (this->isDynamic())
if (this->isDynamic()) return true;
return true; sqlite3_stmt *stmt;
sqlite3_stmt *stmt; sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
sqlite3_prepare(this->db, "SELECT COUNT(*) FROM torrents WHERE info_hash=?", -1, &stmt, NULL); sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL); sqlite3_step(stmt);
sqlite3_step(stmt);
int n = sqlite3_column_int(stmt, 0);
int n = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt);
sqlite3_finalize(stmt);
return (n == 1);
return (n == 1); }
}
void SQLite3Driver::cleanup()
void SQLite3Driver::cleanup() {
{ LOG_INFO("db-sqlite", "Cleaning up...");
int exp = time (NULL) - 7200; // 2 hours, expired. int exp = time (NULL) - 7200; // 2 hours, expired.
int r = 0;
// drop all peers with no activity for 2 hours.
sqlite3_stmt *getTables; // drop all peers with no activity for 2 hours.
// torrent table names: t<hex-of-sha-1> sqlite3_stmt *getTables;
sqlite3_prepare(this->db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 't________________________________________'", -1, &getTables, NULL); // torrent table names: t<hex-of-sha-1>
r = sqlite3_prepare(this->db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 't________________________________________'", -1, &getTables, NULL);
uint8_t buff [20]; if (r != SQLITE_OK) {
sqlite3_stmt *updateStats; LOG_ERR("db-sqlite", "Failed fetch tables from DB for cleanup.");
assert (sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL) == SQLITE_OK); return;
}
while (sqlite3_step(getTables) == SQLITE_ROW) uint8_t buff [20];
{ sqlite3_stmt *updateStats;
char* tblN = (char*)sqlite3_column_text(getTables, 0); r = sqlite3_prepare(this->db, "REPLACE INTO stats (info_hash,seeders,leechers,last_mod) VALUES (?,?,?,?)", -1, &updateStats, NULL);
stringstream sStr; if (r != SQLITE_OK) {
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp; LOG_ERR("db-sqlite", "Failed to prepare update stats query.");
return;
assert (sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL) == SQLITE_OK); }
sStr.str (string()); while (sqlite3_step(getTables) == SQLITE_ROW)
sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0"; {
char* tblN = (char*)sqlite3_column_text(getTables, 0);
sqlite3_stmt *collectStats; stringstream sStr;
sStr << "DELETE FROM " << tblN << " WHERE last_seen<" << exp;
sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
r = sqlite3_exec(this->db, sStr.str().c_str(), NULL, NULL, NULL);
if (sqlite3_errcode(this->db) != SQLITE_OK) if (r != SQLITE_OK) {
{ LOG_ERR("db-sqlite", "Failed to execute cleanup for table '" << tblN << "'.");
// TODO: Log this error continue;
} }
int seeders = 0, leechers = 0; sStr.str (string());
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results. sStr << "SELECT left,COUNT(*) FROM " << tblN << " GROUP BY left==0";
{
if (sqlite3_column_int(collectStats, 0) == 0) sqlite3_stmt *collectStats;
seeders = sqlite3_column_int (collectStats, 1);
else r = sqlite3_prepare(this->db, sStr.str().c_str(), sStr.str().length(), &collectStats, NULL);
leechers = sqlite3_column_int (collectStats, 1);
} if (r != SQLITE_OK)
sqlite3_finalize(collectStats); {
LOG_ERR("db-sqlite", "Failed while trying to prepare stats query for '" << tblN << "', sqlite returned " << r);
sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL); continue;
sqlite3_bind_int(updateStats, 2, seeders); }
sqlite3_bind_int(updateStats, 3, leechers);
sqlite3_bind_int(updateStats, 4, time (NULL)); int seeders = 0, leechers = 0;
while (sqlite3_step(collectStats) == SQLITE_ROW) // expecting two results.
sqlite3_step(updateStats); {
sqlite3_reset (updateStats); if (sqlite3_column_int(collectStats, 0) == 0)
} seeders = sqlite3_column_int (collectStats, 1);
sqlite3_finalize(updateStats); else
sqlite3_finalize(getTables); leechers = sqlite3_column_int (collectStats, 1);
} }
sqlite3_finalize(collectStats);
bool SQLite3Driver::removeTorrent(uint8_t info_hash[20])
{ sqlite3_bind_blob(updateStats, 1, _hash_to_bin((const char*)(tblN + 1), buff), 20, NULL);
// if non-dynamic, remove from table sqlite3_bind_int(updateStats, 2, seeders);
sqlite3_stmt *stmt; sqlite3_bind_int(updateStats, 3, leechers);
sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL); sqlite3_bind_int(updateStats, 4, time (NULL));
sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_step(stmt); sqlite3_step(updateStats);
sqlite3_finalize(stmt); sqlite3_reset (updateStats);
}
// remove from stats sqlite3_finalize(updateStats);
sqlite3_stmt *rmS; sqlite3_finalize(getTables);
if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK) }
{
sqlite3_finalize(rmS); bool SQLite3Driver::removeTorrent(uint8_t info_hash[20]) {
return false; // if non-dynamic, remove from table
} sqlite3_stmt *stmt;
sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL); sqlite3_prepare(this->db, "DELETE FROM torrents WHERE info_hash=?", -1, &stmt, NULL);
sqlite3_step(rmS); sqlite3_bind_blob(stmt, 1, info_hash, 20, NULL);
sqlite3_finalize(rmS); sqlite3_step(stmt);
sqlite3_finalize(stmt);
// remove table
string str = "DROP TABLE IF EXISTS 't"; // remove from stats
char buff [41]; sqlite3_stmt *rmS;
str += _to_hex_str(info_hash, buff); if (sqlite3_prepare(this->db, "DELETE FROM stats WHERE info_hash=?", -1, &rmS, NULL) != SQLITE_OK)
str += "'"; {
sqlite3_finalize(rmS);
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL); return false;
}
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Torrent removed."; sqlite3_bind_blob(rmS, 1, (const void*)info_hash, 20, NULL);
sqlite3_step(rmS);
return true; sqlite3_finalize(rmS);
}
// remove table
bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port) string str = "DROP TABLE IF EXISTS 't";
{ char buff [41];
string sql; str += _to_hex_str(info_hash, buff);
char xHash [50]; str += "'";
sqlite3_stmt *stmt;
sqlite3_exec(this->db, str.c_str(), NULL, NULL, NULL);
_to_hex_str (info_hash, xHash);
return true;
sql += "DELETE FROM 't"; }
sql += xHash;
sql += "' WHERE ip=? AND port=? AND peer_id=?"; bool SQLite3Driver::removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port) {
string sql;
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL); char xHash [50];
sqlite3_stmt *stmt;
sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL); _to_hex_str (info_hash, xHash);
sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
sql += "DELETE FROM 't";
sqlite3_step(stmt); sql += xHash;
sql += "' WHERE ip=? AND port=? AND peer_id=?";
sqlite3_finalize(stmt);
sqlite3_prepare (this->db, sql.c_str(), sql.length(), &stmt, NULL);
return true;
} sqlite3_bind_blob(stmt, 0, (const void*)&ip, 4, NULL);
sqlite3_bind_blob(stmt, 1, (const void*)&port, 2, NULL);
static uint64_t _genCiD (uint32_t ip, uint16_t port) sqlite3_bind_blob(stmt, 2, (const void*)peer_id, 20, NULL);
{
uint64_t x; sqlite3_step(stmt);
x = (time(NULL) / 3600) * port; // x will probably overload.
x = (ip ^ port); sqlite3_finalize(stmt);
x <<= 16;
x |= (~port); return true;
return x; }
}
static uint64_t _genCiD (uint32_t ip, uint16_t port) {
bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port) uint64_t x;
{ x = (time(NULL) / 3600) * port; // x will probably overload.
*connectionId = _genCiD(ip, port); x = (ip ^ port);
return true; x <<= 16;
} x |= (~port);
return x;
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port) }
{
if (cId == _genCiD(ip, port)) bool SQLite3Driver::genConnectionId (uint64_t *connectionId, uint32_t ip, uint16_t port) {
return true; *connectionId = _genCiD(ip, port);
else return true;
return false; }
}
bool SQLite3Driver::verifyConnectionId(uint64_t cId, uint32_t ip, uint16_t port) {
SQLite3Driver::~SQLite3Driver() if (cId == _genCiD(ip, port))
{ return true;
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Closing SQLite"; else
sqlite3_close(this->db); return false;
} }
};
}; SQLite3Driver::~SQLite3Driver() {
sqlite3_close(this->db);
}
};
};

View file

@ -1,56 +1,55 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef DATABASE_H_ #ifndef DATABASE_H_
#define DATABASE_H_ #define DATABASE_H_
#include <stdint.h> #include <stdint.h>
#include "database.hpp" #include "database.hpp"
#include <sqlite3.h> #include <sqlite3.h>
namespace UDPT namespace UDPT
{ {
namespace Data namespace Data
{ {
class SQLite3Driver : public DatabaseDriver class SQLite3Driver : public DatabaseDriver
{ {
public: public:
SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false); SQLite3Driver(const boost::program_options::variables_map& conf, bool isDyn = false);
bool addTorrent(uint8_t info_hash[20]); bool addTorrent(uint8_t info_hash[20]);
bool removeTorrent(uint8_t info_hash[20]); bool removeTorrent(uint8_t info_hash[20]);
bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port); bool genConnectionId(uint64_t *connId, uint32_t ip, uint16_t port);
bool verifyConnectionId(uint64_t connId, uint32_t ip, uint16_t port); bool verifyConnectionId(uint64_t connId, uint32_t ip, uint16_t port);
bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event); bool updatePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port, int64_t downloaded, int64_t left, int64_t uploaded, enum TrackerEvents event);
bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port); bool removePeer(uint8_t peer_id [20], uint8_t info_hash [20], uint32_t ip, uint16_t port);
bool getTorrentInfo(TorrentEntry *e); bool getTorrentInfo(TorrentEntry *e);
bool isTorrentAllowed(uint8_t info_hash[20]); bool isTorrentAllowed(uint8_t info_hash[20]);
bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe); bool getPeers(uint8_t info_hash [20], int *max_count, PeerEntry *pe);
void cleanup(); void cleanup();
virtual ~SQLite3Driver(); virtual ~SQLite3Driver();
private: private:
sqlite3 *db; sqlite3 *db;
boost::log::sources::severity_channel_logger_mt<> m_logger;
void doSetup();
void doSetup(); };
}; };
}; };
};
#endif /* DATABASE_H_ */
#endif /* DATABASE_H_ */

View file

@ -4,73 +4,73 @@
namespace UDPT namespace UDPT
{ {
class UDPTException class UDPTException
{ {
public: public:
UDPTException(const char* errorMsg, int errorCode = 0) : m_error(errorMsg), m_errorCode(errorCode) UDPTException(const char* errorMsg, int errorCode = 0) : m_error(errorMsg), m_errorCode(errorCode)
{ {
} }
UDPTException(int errorCode = 0) : m_errorCode(errorCode), m_error("") UDPTException(int errorCode = 0) : m_errorCode(errorCode), m_error("")
{ {
} }
virtual const char* what() const virtual const char* what() const
{ {
return m_error; return m_error;
} }
virtual int getErrorCode() const virtual int getErrorCode() const
{ {
return m_errorCode; return m_errorCode;
} }
virtual ~UDPTException() virtual ~UDPTException()
{ {
} }
protected: protected:
const char* m_error; const char* m_error;
const int m_errorCode; const int m_errorCode;
}; };
class OSError : public UDPTException class OSError : public UDPTException
{ {
public: public:
OSError(int errorCode OSError(int errorCode
#ifdef WIN32 #ifdef WIN32
= ::GetLastError() = ::GetLastError()
#endif #endif
) : UDPTException(errorCode) ) : UDPTException(errorCode)
{ {
} }
virtual ~OSError() {} virtual ~OSError() {}
const char* what() const const char* what() const
{ {
if (m_errorMessage.length() > 0) if (m_errorMessage.length() > 0)
{ {
return m_errorMessage.c_str(); return m_errorMessage.c_str();
} }
#ifdef WIN32 #ifdef WIN32
char *buffer = nullptr; char *buffer = nullptr;
DWORD msgLen = ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, m_errorCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast<LPSTR>(&buffer), 1, NULL); DWORD msgLen = ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, m_errorCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast<LPSTR>(&buffer), 1, NULL);
std::shared_ptr<void> formatStr = std::shared_ptr<void>( std::shared_ptr<void> formatStr = std::shared_ptr<void>(
buffer, buffer,
::LocalFree); ::LocalFree);
m_errorMessage = std::string(reinterpret_cast<char*>(formatStr.get())); m_errorMessage = std::string(reinterpret_cast<char*>(formatStr.get()));
return m_errorMessage.c_str(); return m_errorMessage.c_str();
#else #else
return "OSError"; return "OSError";
#endif #endif
} }
private: private:
// allow to generate a message only when needed. // allow to generate a message only when needed.
mutable std::string m_errorMessage; mutable std::string m_errorMessage;
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -1,170 +1,170 @@
/* /*
* Copyright © 2013-2016 Naim A. * Copyright © 2013-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <map> #include <map>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <list> #include <list>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include "../multiplatform.h" #include "../multiplatform.h"
using namespace std; using namespace std;
#define REQUEST_BUFFER_SIZE 2048 #define REQUEST_BUFFER_SIZE 2048
namespace UDPT namespace UDPT
{ {
namespace Server namespace Server
{ {
class ServerException class ServerException
{ {
public: public:
inline ServerException (int ec) inline ServerException (int ec)
{ {
this->ec = ec; this->ec = ec;
this->em = NULL; this->em = NULL;
} }
inline ServerException (int ec, const char *em) inline ServerException (int ec, const char *em)
{ {
this->ec = ec; this->ec = ec;
this->em = em; this->em = em;
} }
inline const char *getErrorMsg () const inline const char *getErrorMsg () const
{ {
return this->em; return this->em;
} }
inline int getErrorCode () const inline int getErrorCode () const
{ {
return this->ec; return this->ec;
} }
private: private:
int ec; int ec;
const char *em; const char *em;
}; };
class HTTPServer class HTTPServer
{ {
public: public:
class Request class Request
{ {
public: public:
enum RequestMethod enum RequestMethod
{ {
RM_UNKNOWN = 0, RM_UNKNOWN = 0,
RM_GET = 1, RM_GET = 1,
RM_POST = 2 RM_POST = 2
}; };
Request (SOCKET, const SOCKADDR_IN *); Request (SOCKET, const SOCKADDR_IN *);
list<string>* getPath (); list<string>* getPath ();
string getParam (const string key); string getParam (const string key);
multimap<string, string>::iterator getHeader (const string name); multimap<string, string>::iterator getHeader (const string name);
RequestMethod getRequestMethod (); RequestMethod getRequestMethod ();
string getRequestMethodStr (); string getRequestMethodStr ();
string getCookie (const string name); string getCookie (const string name);
const SOCKADDR_IN* getAddress (); const SOCKADDR_IN* getAddress ();
private: private:
const SOCKADDR_IN *addr; const SOCKADDR_IN *addr;
SOCKET conn; SOCKET conn;
struct { struct {
int major; int major;
int minor; int minor;
} httpVer; } httpVer;
struct { struct {
string str; string str;
RequestMethod rm; RequestMethod rm;
} requestMethod; } requestMethod;
list<string> path; list<string> path;
map<string, string> params; map<string, string> params;
map<string, string> cookies; map<string, string> cookies;
multimap<string, string> headers; multimap<string, string> headers;
void parseRequest (); void parseRequest ();
}; };
class Response class Response
{ {
public: public:
Response (SOCKET conn); Response (SOCKET conn);
void setStatus (int, const string); void setStatus (int, const string);
void addHeader (string key, string value); void addHeader (string key, string value);
int writeRaw (const char *data, int len); int writeRaw (const char *data, int len);
void write (const char *data, int len = -1); void write (const char *data, int len = -1);
private: private:
friend class HTTPServer; friend class HTTPServer;
SOCKET conn; SOCKET conn;
int status_code; int status_code;
string status_msg; string status_msg;
multimap<string, string> headers; multimap<string, string> headers;
stringstream msg; stringstream msg;
void finalize (); void finalize ();
}; };
typedef void (reqCallback)(HTTPServer*,Request*,Response*); typedef void (reqCallback)(HTTPServer*,Request*,Response*);
HTTPServer (uint16_t port, int threads); HTTPServer (uint16_t port, int threads);
HTTPServer(const boost::program_options::variables_map& conf); HTTPServer(const boost::program_options::variables_map& conf);
void addApp (list<string> *path, reqCallback *); void addApp (list<string> *path, reqCallback *);
void setData (string, void *); void setData (string, void *);
void* getData (string); void* getData (string);
virtual ~HTTPServer (); virtual ~HTTPServer ();
private: private:
typedef struct appNode typedef struct appNode
{ {
reqCallback *callback; reqCallback *callback;
map<string, appNode> nodes; map<string, appNode> nodes;
} appNode; } appNode;
SOCKET srv; SOCKET srv;
int thread_count; int thread_count;
HANDLE *threads; HANDLE *threads;
bool isRunning; bool isRunning;
appNode rootNode; appNode rootNode;
map<string, void*> customData; map<string, void*> customData;
void init (SOCKADDR_IN &localEndpoint, int threads); void init (SOCKADDR_IN &localEndpoint, int threads);
static void handleConnections (HTTPServer *); static void handleConnections (HTTPServer *);
#ifdef WIN32 #ifdef WIN32
static DWORD _thread_start (LPVOID); static DWORD _thread_start (LPVOID);
#else #else
static void* _thread_start (void*); static void* _thread_start (void*);
#endif #endif
static reqCallback* getRequestHandler (appNode *, list<string> *); static reqCallback* getRequestHandler (appNode *, list<string> *);
}; };
}; };
}; };

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2013-2016 Naim A. * Copyright © 2013-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -25,190 +25,190 @@ using namespace std;
namespace UDPT namespace UDPT
{ {
namespace Server namespace Server
{ {
static uint32_t _getNextIPv4 (string::size_type &i, string &line) static uint32_t _getNextIPv4 (string::size_type &i, string &line)
{ {
string::size_type len = line.length(); string::size_type len = line.length();
char c; char c;
while (i < len) while (i < len)
{ {
c = line.at(i); c = line.at(i);
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
break; break;
i++; i++;
} }
uint32_t ip = 0; uint32_t ip = 0;
for (int n = 0;n < 4;n++) for (int n = 0;n < 4;n++)
{ {
int cn = 0; int cn = 0;
while (i < len) while (i < len)
{ {
c = line.at (i++); c = line.at (i++);
if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3)) if (c == '.' || ((c == ' ' || c == ',' || c == ';') && n == 3))
break; break;
else if (!(c >= '0' && c <= '9')) else if (!(c >= '0' && c <= '9'))
return 0; return 0;
cn *= 10; cn *= 10;
cn += (c - '0'); cn += (c - '0');
} }
ip *= 256; ip *= 256;
ip += cn; ip += cn;
} }
return ip; return ip;
} }
static bool _hex2bin (uint8_t *data, const string str) static bool _hex2bin (uint8_t *data, const string str)
{ {
int len = str.length(); int len = str.length();
if (len % 2 != 0) if (len % 2 != 0)
return false; return false;
char a, b; char a, b;
uint8_t c; uint8_t c;
for (int i = 0;i < len;i+=2) for (int i = 0;i < len;i+=2)
{ {
a = str.at (i); a = str.at (i);
b = str.at (i + 1); b = str.at (i + 1);
c = 0; c = 0;
if (a >= 'a' && a <= 'f') if (a >= 'a' && a <= 'f')
a = (a - 'a') + 10; a = (a - 'a') + 10;
else if (a >= '0' && a <= '9') else if (a >= '0' && a <= '9')
a = (a - '0'); a = (a - '0');
else else
return false; return false;
if (b >= 'a' && b <= 'f') if (b >= 'a' && b <= 'f')
b = (b - 'a') + 10; b = (b - 'a') + 10;
else if (b >= '0' && b <= '9') else if (b >= '0' && b <= '9')
b = (b - '0'); b = (b - '0');
else else
return false; return false;
c = (a * 16) + b; c = (a * 16) + b;
data [i / 2] = c; data [i / 2] = c;
} }
return true; return true;
} }
WebApp::WebApp(std::shared_ptr<HTTPServer> srv, DatabaseDriver *db, const boost::program_options::variables_map& conf) : m_conf(conf), m_server(srv) WebApp::WebApp(std::shared_ptr<HTTPServer> srv, DatabaseDriver *db, const boost::program_options::variables_map& conf) : m_conf(conf), m_server(srv)
{ {
this->db = db; this->db = db;
// TODO: Implement authentication by keys // TODO: Implement authentication by keys
m_server->setData("webapp", this); m_server->setData("webapp", this);
} }
WebApp::~WebApp() WebApp::~WebApp()
{ {
} }
void WebApp::deploy() void WebApp::deploy()
{ {
list<string> path; list<string> path;
m_server->addApp(&path, &WebApp::handleRoot); m_server->addApp(&path, &WebApp::handleRoot);
path.push_back("api"); path.push_back("api");
m_server->addApp(&path, &WebApp::handleAPI); // "/api" m_server->addApp(&path, &WebApp::handleAPI); // "/api"
path.pop_back(); path.pop_back();
path.push_back("announce"); path.push_back("announce");
m_server->addApp(&path, &WebApp::handleAnnounce); m_server->addApp(&path, &WebApp::handleAnnounce);
} }
void WebApp::handleRoot(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp) void WebApp::handleRoot(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{ {
// It would be very appreciated to keep this in the code. // It would be very appreciated to keep this in the code.
resp->write("<html>" resp->write("<html>"
"<head><title>UDPT Torrent Tracker</title></head>" "<head><title>UDPT Torrent Tracker</title></head>"
"<body>" "<body>"
"<div style=\"vertical-align:top;\">This tracker is running on UDPT Software.</div>" "<div style=\"vertical-align:top;\">This tracker is running on UDPT Software.</div>"
"<br /><hr /><div style=\"text-align:center;font-size:small;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div>" "<br /><hr /><div style=\"text-align:center;font-size:small;\"><a href=\"http://github.com/naim94a/udpt\">UDPT</a></div>"
"</body>" "</body>"
"</html>"); "</html>");
} }
void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp) void WebApp::doRemoveTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{ {
string strHash = req->getParam("hash"); string strHash = req->getParam("hash");
if (strHash.length() != 40) if (strHash.length() != 40)
{ {
resp->write("{\"error\":\"Hash length must be 40 characters.\"}"); resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
return; return;
} }
uint8_t hash [20]; uint8_t hash [20];
if (!_hex2bin(hash, strHash)) if (!_hex2bin(hash, strHash))
{ {
resp->write("{\"error\":\"invalid info_hash.\"}"); resp->write("{\"error\":\"invalid info_hash.\"}");
return; return;
} }
if (this->db->removeTorrent(hash)) if (this->db->removeTorrent(hash))
resp->write("{\"success\":true}"); resp->write("{\"success\":true}");
else else
resp->write("{\"error\":\"failed to remove torrent from DB\"}"); resp->write("{\"error\":\"failed to remove torrent from DB\"}");
} }
void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp) void WebApp::doAddTorrent (HTTPServer::Request *req, HTTPServer::Response *resp)
{ {
std::string strHash = req->getParam("hash"); std::string strHash = req->getParam("hash");
if (strHash.length() != 40) if (strHash.length() != 40)
{ {
resp->write("{\"error\":\"Hash length must be 40 characters.\"}"); resp->write("{\"error\":\"Hash length must be 40 characters.\"}");
return; return;
} }
uint8_t hash [20]; uint8_t hash [20];
if (!_hex2bin(hash, strHash)) if (!_hex2bin(hash, strHash))
{ {
resp->write("{\"error\":\"invalid info_hash.\"}"); resp->write("{\"error\":\"invalid info_hash.\"}");
return; return;
} }
if (this->db->addTorrent(hash)) if (this->db->addTorrent(hash))
resp->write("{\"success\":true}"); resp->write("{\"success\":true}");
else else
resp->write("{\"error\":\"failed to add torrent to DB\"}"); resp->write("{\"error\":\"failed to add torrent to DB\"}");
} }
void WebApp::handleAnnounce (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp) void WebApp::handleAnnounce (HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{ {
resp->write("d14:failure reason42:this is a UDP tracker, not a HTTP tracker.e"); resp->write("d14:failure reason42:this is a UDP tracker, not a HTTP tracker.e");
} }
void WebApp::handleAPI(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp) void WebApp::handleAPI(HTTPServer *srv, HTTPServer::Request *req, HTTPServer::Response *resp)
{ {
if (req->getAddress()->sin_family != AF_INET) if (req->getAddress()->sin_family != AF_INET)
{ {
throw ServerException (0, "IPv4 supported Only."); throw ServerException (0, "IPv4 supported Only.");
} }
WebApp *app = (WebApp*)srv->getData("webapp"); WebApp *app = (WebApp*)srv->getData("webapp");
if (app == NULL) if (app == NULL)
throw ServerException(0, "WebApp object wasn't found"); throw ServerException(0, "WebApp object wasn't found");
if (req->getAddress()->sin_addr.s_addr != 0x0100007f) if (req->getAddress()->sin_addr.s_addr != 0x0100007f)
{ {
resp->setStatus(403, "Forbidden"); resp->setStatus(403, "Forbidden");
resp->write("Access Denied. Only 127.0.0.1 can access this method."); resp->write("Access Denied. Only 127.0.0.1 can access this method.");
return; return;
} }
std::string action = req->getParam("action"); std::string action = req->getParam("action");
if (action == "add") if (action == "add")
app->doAddTorrent(req, resp); app->doAddTorrent(req, resp);
else if (action == "remove") else if (action == "remove")
app->doRemoveTorrent(req, resp); app->doRemoveTorrent(req, resp);
else else
{ {
resp->write("{\"error\":\"unknown action\"}"); resp->write("{\"error\":\"unknown action\"}");
} }
} }
}; };
}; };

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2013-2016 Naim A. * Copyright © 2013-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -33,27 +33,27 @@ using namespace UDPT::Data;
namespace UDPT namespace UDPT
{ {
namespace Server namespace Server
{ {
class WebApp class WebApp
{ {
public: public:
WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf); WebApp(std::shared_ptr<HTTPServer> , DatabaseDriver *, const boost::program_options::variables_map& conf);
virtual ~WebApp(); virtual ~WebApp();
void deploy (); void deploy ();
private:
std::shared_ptr<HTTPServer> m_server;
UDPT::Data::DatabaseDriver *db;
const boost::program_options::variables_map& m_conf;
static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*); private:
static void handleAnnounce (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*); std::shared_ptr<HTTPServer> m_server;
static void handleAPI (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*); UDPT::Data::DatabaseDriver *db;
const boost::program_options::variables_map& m_conf;
void doAddTorrent (HTTPServer::Request*, HTTPServer::Response*); static void handleRoot (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
void doRemoveTorrent (HTTPServer::Request*, HTTPServer::Response*); static void handleAnnounce (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
}; static void handleAPI (HTTPServer*,HTTPServer::Request*, HTTPServer::Response*);
};
void doAddTorrent (HTTPServer::Request*, HTTPServer::Response*);
void doRemoveTorrent (HTTPServer::Request*, HTTPServer::Response*);
};
};
}; };

140
src/logging.cpp Normal file
View file

@ -0,0 +1,140 @@
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#include "logging.hpp"
#include <sstream>
#include <chrono>
#include <cstring>
#include <iostream>
namespace UDPT {
namespace Logging {
Logger::Logger() : m_cleaningUp(false), m_workerThread(Logger::worker, this), m_minLogLevel(Severity::FATAL) {
}
Logger::~Logger() {
try {
// prevent new log messages from entering queue.
m_cleaningUp = true;
// tell thread to exit
m_runningCondition.notify_one();
// wait for worker to terminate
m_workerThread.join();
// flush any remaining logs
flush();
// flush iostreams
for (std::vector<std::pair<std::ostream*, Severity>>::const_iterator it = m_outputStreams.begin(); it != m_outputStreams.end(); ++it) {
it->first->flush();
}
} catch (...) {
// can't do much here... this is a logging class...
}
}
Logger& Logger::getLogger() {
static Logger instance;
return instance;
}
void Logger::log(Severity severity, const std::string& channel, const std::string& message) {
if (severity < m_minLogLevel) {
return;
}
m_queue.Push(LogEntry{
.when=std::chrono::system_clock::now(),
.severity=severity,
.channel=channel,
.message=message
});
}
void Logger::addStream(std::ostream *stream, Severity severity) {
m_minLogLevel = m_minLogLevel > severity ? severity : m_minLogLevel;
m_outputStreams.push_back(std::pair<std::ostream*, Severity>(stream, severity));
}
void Logger::flush() {
while (!m_queue.IsEmpty()) {
LogEntry entry = m_queue.Pop();
std::stringstream sstream;
//TODO: Write the log time in a more elegant manner.
time_t timestamp = std::chrono::system_clock::to_time_t(entry.when);
char* time_buffer = ctime(&timestamp);
time_buffer[strlen(time_buffer) - 1] = '\0';
sstream << time_buffer << "\t";
switch (entry.severity) {
case Severity::DEBUG:
sstream << "DEBUG";
break;
case Severity::INFO:
sstream << "INFO ";
break;
case Severity::WARNING:
sstream << "WARN ";
break;
case Severity::ERROR:
sstream << "ERROR";
break;
case Severity::FATAL:
sstream << "FATAL";
break;
}
sstream << " [" << entry.channel << "]\t" << entry.message;
const std::string& result_log = sstream.str();
for (std::vector<std::pair<std::ostream*, Severity>>::const_iterator it = m_outputStreams.begin(); it != m_outputStreams.end(); ++it) {
if (entry.severity < it->second) {
// message severity isn't high enough for this logger.
continue;
}
std::ostream& current_stream = *(it->first);
// catch an exception in case we get a broken pipe or something...
try {
current_stream << result_log << std::endl;
} catch (...) {
// do nothing.
}
}
}
}
void Logger::worker(Logger *me) {
std::unique_lock<std::mutex> lk (me->m_runningMutex);
while (true) {
me->flush();
if (std::cv_status::no_timeout == me->m_runningCondition.wait_for(lk, std::chrono::seconds(5))) {
break;
}
}
}
}
}

83
src/logging.hpp Normal file
View file

@ -0,0 +1,83 @@
/*
* Copyright © 2012-2017 Naim A.
*
* This file is part of UDPT.
*
* UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <ostream>
#include <thread>
#include <atomic>
#include <condition_variable>
#include "MessageQueue.hpp"
#define LOG(severity, channel, message) \
{\
std::stringstream __sstream; \
__sstream << message; \
UDPT::Logging::Logger::getLogger().log(severity, channel, __sstream.str()); \
}
#define LOG_INFO(channel, message) LOG(UDPT::Logging::Severity::INFO, channel, message)
#define LOG_DEBUG(channel, message) LOG(UDPT::Logging::Severity::DEBUG, channel, message)
#define LOG_WARN(channel, message) LOG(UDPT::Logging::Severity::WARNING, channel, message)
#define LOG_ERR(channel, message) LOG(UDPT::Logging::Severity::ERROR, channel, message)
#define LOG_FATAL(channel, message) LOG(UDPT::Logging::Severity::FATAL, channel, message)
namespace UDPT {
namespace Logging {
enum Severity {
UNSET = 0,
DEBUG = 10,
INFO = 20,
WARNING = 30,
ERROR = 40,
FATAL = 50
};
struct LogEntry {
const std::chrono::time_point<std::chrono::system_clock> when;
Severity severity;
const std::string channel;
const std::string message;
};
class Logger {
public:
static Logger& getLogger();
void log(Severity severity, const std::string& channel, const std::string& message);
void addStream(std::ostream *, Severity minSeverity=INFO);
private:
Logger();
virtual ~Logger();
static void worker(Logger*);
void flush();
std::vector<std::pair<std::ostream*, UDPT::Logging::Severity>> m_outputStreams;
UDPT::Utils::MessageQueue<struct LogEntry> m_queue;
std::thread m_workerThread;
std::atomic_bool m_cleaningUp;
std::mutex m_runningMutex;
std::condition_variable m_runningCondition;
Severity m_minLogLevel;
};
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -25,8 +25,6 @@
#include <memory> #include <memory>
#include <algorithm> #include <algorithm>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include "multiplatform.h" #include "multiplatform.h"
#include "udpTracker.hpp" #include "udpTracker.hpp"
@ -34,35 +32,39 @@
#include "http/webapp.hpp" #include "http/webapp.hpp"
#include "tracker.hpp" #include "tracker.hpp"
#include "service.hpp" #include "service.hpp"
#include "logging.hpp"
static void _signal_handler(int sig) extern "C" void _signal_handler(int sig)
{ {
switch (sig) switch (sig) {
{ case SIGTERM:
case SIGTERM: case SIGQUIT:
UDPT::Tracker::getInstance().stop(); case SIGINT: {
break; LOG_INFO("core", "Received signal " << sig << ", requesting to stop tracker");
} UDPT::Tracker::getInstance().stop();
break;
}
}
} }
#ifdef linux #ifdef linux
static void daemonize(const boost::program_options::variables_map& conf) static void daemonize(const boost::program_options::variables_map& conf)
{ {
if (1 == ::getppid()) return; // already a daemon if (1 == ::getppid()) return; // already a daemon
int r = ::fork(); int r = ::fork();
if (0 > r) ::exit(-1); // failed to daemonize. if (0 > r) ::exit(-1); // failed to daemonize.
if (0 < r) ::exit(0); // parent exists. if (0 < r) ::exit(0); // parent exists.
::umask(0); ::umask(0);
::setsid(); ::setsid();
// close all fds. // close all fds.
for (int i = ::getdtablesize(); i >=0; --i) for (int i = ::getdtablesize(); i >=0; --i)
{ {
::close(i); ::close(i);
} }
::chdir(conf["daemon.chdir"].as<std::string>().c_str()); ::chdir(conf["daemon.chdir"].as<std::string>().c_str());
} }
#endif #endif
@ -70,159 +72,163 @@ static void daemonize(const boost::program_options::variables_map& conf)
#ifdef WIN32 #ifdef WIN32
void _close_wsa() void _close_wsa()
{ {
::WSACleanup(); ::WSACleanup();
} }
#endif #endif
#ifdef TEST
int real_main(int argc, char *argv[])
#else
int main(int argc, char *argv[]) int main(int argc, char *argv[])
#endif
{ {
#ifdef WIN32 #ifdef WIN32
WSADATA wsadata; WSADATA wsadata;
::WSAStartup(MAKEWORD(2, 2), &wsadata); ::WSAStartup(MAKEWORD(2, 2), &wsadata);
::atexit(_close_wsa); ::atexit(_close_wsa);
#endif #endif
boost::program_options::options_description commandLine("Command line options"); boost::program_options::options_description commandLine("Command line options");
commandLine.add_options() commandLine.add_options()
("help,h", "produce help message") ("help,h", "produce help message")
("all-help", "displays all help") ("all-help", "displays all help")
("test,t", "test configuration file") ("test,t", "test configuration file")
("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use") ("config,c", boost::program_options::value<std::string>()->default_value("/etc/udpt.conf"), "configuration file to use")
#ifdef linux #ifdef linux
("interactive,i", "doesn't start as daemon") ("interactive,i", "doesn't start as daemon")
#endif #endif
#ifdef WIN32 #ifdef WIN32
("service,s", boost::program_options::value<std::string>(), "start/stop/install/uninstall service") ("service,s", boost::program_options::value<std::string>(), "start/stop/install/uninstall service")
#endif #endif
; ;
const boost::program_options::options_description& configOptions = Tracker::getConfigOptions(); const boost::program_options::options_description& configOptions = Tracker::getConfigOptions();
boost::program_options::variables_map var_map; boost::program_options::variables_map var_map;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, commandLine), var_map); boost::program_options::store(boost::program_options::parse_command_line(argc, argv, commandLine), var_map);
boost::program_options::notify(var_map); boost::program_options::notify(var_map);
if (var_map.count("help")) if (var_map.count("help"))
{ {
std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl std::cout << "UDP Tracker (UDPT) " << VERSION << " (" << PLATFORM << ")" << std::endl
<< "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl << "Copyright 2012-2016 Naim A. <naim94a@gmail.com>" << std::endl
<< "Build Date: " << __DATE__ << std::endl << std::endl; << "Build Date: " << __DATE__ << std::endl << std::endl;
std::cout << commandLine << std::endl;
return 0;
}
if (var_map.count("all-help")) std::cout << commandLine << std::endl;
{ return 0;
std::cout << commandLine << std::endl; }
std::cout << configOptions << std::endl;
return 0;
}
std::string config_filename(var_map["config"].as<std::string>()); if (var_map.count("all-help"))
bool isTest = (0 != var_map.count("test")); {
std::cout << commandLine << std::endl;
std::cout << configOptions << std::endl;
return 0;
}
if (var_map.count("config")) std::string config_filename(var_map["config"].as<std::string>());
{ bool isTest = (0 != var_map.count("test"));
try
{
boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(config_filename.c_str(), configOptions);
boost::program_options::store(
parsed_options,
var_map);
}
catch (const boost::program_options::error& ex)
{
std::cerr << "ERROR: " << ex.what() << std::endl;
return -1;
}
if (isTest) if (var_map.count("config"))
{ {
std::cout << "Config OK" << std::endl; try
return 0; {
} boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(config_filename.c_str(), configOptions);
} boost::program_options::store(
parsed_options,
var_map);
}
catch (const boost::program_options::error& ex)
{
std::cerr << "ERROR: " << ex.what() << std::endl;
return -1;
}
boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "main"); if (isTest)
Tracker::setupLogging(var_map, logger); {
std::cout << "Config OK" << std::endl;
return 0;
}
}
#ifdef linux #ifdef linux
if (!var_map.count("interactive")) if (!var_map.count("interactive"))
{ {
daemonize(var_map); daemonize(var_map);
} }
::signal(SIGTERM, _signal_handler); ::signal(SIGTERM, _signal_handler);
::signal(SIGINT, _signal_handler);
#endif #endif
#ifdef WIN32 #ifdef WIN32
UDPT::Service svc(var_map); UDPT::Service svc(var_map);
if (var_map.count("service")) if (var_map.count("service"))
{ {
const std::string& action = var_map["service"].as<std::string>(); const std::string& action = var_map["service"].as<std::string>();
try try
{ {
if ("install" == action) if ("install" == action)
{ {
std::cerr << "Installing service..." << std::endl; std::cerr << "Installing service..." << std::endl;
svc.install(var_map["config"].as<std::string>()); svc.install(var_map["config"].as<std::string>());
std::cerr << "Installed." << std::endl; std::cerr << "Installed." << std::endl;
} }
else if ("uninstall" == action) else if ("uninstall" == action)
{ {
std::cerr << "Removing service..." << std::endl; std::cerr << "Removing service..." << std::endl;
svc.uninstall(); svc.uninstall();
std::cerr << "Removed." << std::endl; std::cerr << "Removed." << std::endl;
} }
else if ("start" == action) else if ("start" == action)
{ {
svc.start(); svc.start();
} }
else if ("stop" == action) else if ("stop" == action)
{ {
svc.stop(); svc.stop();
} }
else else
{ {
std::cerr << "No such service command." << std::endl; std::cerr << "No such service command." << std::endl;
return -1; return -1;
} }
} }
catch (const UDPT::OSError& ex) catch (const UDPT::OSError& ex)
{ {
std::cerr << "An operating system error occurred: " << ex.what() << std::endl; std::cerr << "An operating system error occurred: " << ex.what() << std::endl;
return -1; return -1;
} }
return 0; return 0;
} }
try try
{ {
svc.setup(); svc.setup();
return 0; return 0;
} }
catch (const OSError& err) catch (const OSError& err)
{ {
if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT != err.getErrorCode()) if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT != err.getErrorCode())
{ {
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to start as a Windows service: (" << err.getErrorCode() << "): " << err.what(); BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to start as a Windows service: (" << err.getErrorCode() << "): " << err.what();
return -1; return -1;
} }
} }
#endif #endif
try try
{ {
Tracker& tracker = UDPT::Tracker::getInstance(); Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(var_map); tracker.start(var_map);
tracker.wait(); tracker.wait();
} }
catch (const UDPT::UDPTException& ex) catch (const UDPT::UDPTException& ex)
{ {
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "UDPT exception: (" << ex.getErrorCode() << "): " << ex.what(); std::cerr << "UDPT exception: (" << ex.getErrorCode() << "): " << ex.what();
return -1; return -1;
} }
return 0; LOG_INFO("core", "UDPT terminated.");
return 0;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -23,234 +23,234 @@
namespace UDPT namespace UDPT
{ {
SERVICE_STATUS_HANDLE Service::s_hServiceStatus = nullptr; SERVICE_STATUS_HANDLE Service::s_hServiceStatus = nullptr;
SERVICE_STATUS Service::s_serviceStatus = { 0 }; SERVICE_STATUS Service::s_serviceStatus = { 0 };
Service::Service(const boost::program_options::variables_map& conf) : m_conf(conf) Service::Service(const boost::program_options::variables_map& conf) : m_conf(conf)
{ {
} }
Service::~Service() Service::~Service()
{ {
} }
void Service::install(const std::string& config_path) void Service::install(const std::string& config_path)
{ {
std::string& binaryPath = getFilename(); std::string& binaryPath = getFilename();
binaryPath = "\"" + binaryPath + "\" -c \"" + config_path + "\""; binaryPath = "\"" + binaryPath + "\" -c \"" + config_path + "\"";
std::shared_ptr<void> svcMgr = getServiceManager(SC_MANAGER_CREATE_SERVICE); std::shared_ptr<void> svcMgr = getServiceManager(SC_MANAGER_CREATE_SERVICE);
{ {
SC_HANDLE installedService = ::CreateService(reinterpret_cast<SC_HANDLE>(svcMgr.get()), SC_HANDLE installedService = ::CreateService(reinterpret_cast<SC_HANDLE>(svcMgr.get()),
m_conf["service.name"].as<std::string>().c_str(), m_conf["service.name"].as<std::string>().c_str(),
"UDPT Tracker", "UDPT Tracker",
SC_MANAGER_CREATE_SERVICE, SC_MANAGER_CREATE_SERVICE,
SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL, SERVICE_ERROR_NORMAL,
binaryPath.c_str(), binaryPath.c_str(),
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL NULL
); );
if (nullptr == installedService) if (nullptr == installedService)
{ {
throw OSError(); throw OSError();
} }
::CloseServiceHandle(installedService); ::CloseServiceHandle(installedService);
} }
} }
void Service::uninstall() void Service::uninstall()
{ {
std::shared_ptr<void> service = getService(DELETE); std::shared_ptr<void> service = getService(DELETE);
BOOL bRes = ::DeleteService(reinterpret_cast<SC_HANDLE>(service.get())); BOOL bRes = ::DeleteService(reinterpret_cast<SC_HANDLE>(service.get()));
if (FALSE == bRes) if (FALSE == bRes)
{ {
throw OSError(); throw OSError();
} }
} }
void Service::start() void Service::start()
{ {
std::shared_ptr<void> hSvc = getService(SERVICE_START); std::shared_ptr<void> hSvc = getService(SERVICE_START);
BOOL bRes = ::StartService(reinterpret_cast<SC_HANDLE>(hSvc.get()), 0, NULL); BOOL bRes = ::StartService(reinterpret_cast<SC_HANDLE>(hSvc.get()), 0, NULL);
if (FALSE == bRes) if (FALSE == bRes)
{ {
throw OSError(); throw OSError();
} }
} }
void Service::stop() void Service::stop()
{ {
SERVICE_STATUS status = { 0 }; SERVICE_STATUS status = { 0 };
std::shared_ptr<void> hSvc = getService(SERVICE_STOP); std::shared_ptr<void> hSvc = getService(SERVICE_STOP);
BOOL bRes = ::ControlService(reinterpret_cast<SC_HANDLE>(hSvc.get()), SERVICE_CONTROL_STOP, &status); BOOL bRes = ::ControlService(reinterpret_cast<SC_HANDLE>(hSvc.get()), SERVICE_CONTROL_STOP, &status);
if (FALSE == bRes) if (FALSE == bRes)
{ {
throw OSError(); throw OSError();
} }
} }
void Service::setup() void Service::setup()
{ {
SERVICE_TABLE_ENTRY service[] = { SERVICE_TABLE_ENTRY service[] = {
{ const_cast<char*>(m_conf["service.name"].as<std::string>().c_str()), reinterpret_cast<LPSERVICE_MAIN_FUNCTION>(&Service::serviceMain) }, { const_cast<char*>(m_conf["service.name"].as<std::string>().c_str()), reinterpret_cast<LPSERVICE_MAIN_FUNCTION>(&Service::serviceMain) },
{0, 0} {0, 0}
}; };
if (FALSE == ::StartServiceCtrlDispatcher(service)) if (FALSE == ::StartServiceCtrlDispatcher(service))
{ {
throw OSError(); throw OSError();
} }
} }
DWORD Service::handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context) DWORD Service::handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context)
{ {
switch (controlCode) switch (controlCode)
{ {
case SERVICE_CONTROL_INTERROGATE: case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR; return NO_ERROR;
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_STOP:
{ {
reportServiceStatus(SERVICE_STOP_PENDING, 0, 3000); reportServiceStatus(SERVICE_STOP_PENDING, 0, 3000);
Tracker::getInstance().stop(); Tracker::getInstance().stop();
return NO_ERROR; return NO_ERROR;
} }
default: default:
return ERROR_CALL_NOT_IMPLEMENTED; return ERROR_CALL_NOT_IMPLEMENTED;
} }
} }
void Service::reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint) void Service::reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint)
{ {
static DWORD checkpoint = 1; static DWORD checkpoint = 1;
if (currentState == SERVICE_STOPPED || currentState == SERVICE_RUNNING) if (currentState == SERVICE_STOPPED || currentState == SERVICE_RUNNING)
{ {
checkpoint = 0; checkpoint = 0;
} }
else else
{ {
++checkpoint; ++checkpoint;
} }
switch (currentState) switch (currentState)
{ {
case SERVICE_RUNNING: case SERVICE_RUNNING:
s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
break; break;
default: default:
s_serviceStatus.dwControlsAccepted = 0; s_serviceStatus.dwControlsAccepted = 0;
} }
s_serviceStatus.dwCheckPoint = checkpoint; s_serviceStatus.dwCheckPoint = checkpoint;
s_serviceStatus.dwCurrentState = currentState; s_serviceStatus.dwCurrentState = currentState;
s_serviceStatus.dwWin32ExitCode = dwExitCode; s_serviceStatus.dwWin32ExitCode = dwExitCode;
s_serviceStatus.dwWaitHint = dwWaitHint; s_serviceStatus.dwWaitHint = dwWaitHint;
::SetServiceStatus(s_hServiceStatus, &s_serviceStatus); ::SetServiceStatus(s_hServiceStatus, &s_serviceStatus);
} }
VOID Service::serviceMain(DWORD argc, LPCSTR argv[]) VOID Service::serviceMain(DWORD argc, LPCSTR argv[])
{ {
boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "service"); boost::log::sources::severity_channel_logger_mt<> logger(boost::log::keywords::channel = "service");
wchar_t *commandLine = ::GetCommandLineW();
int argCount = 0;
std::shared_ptr<LPWSTR> args(::CommandLineToArgvW(commandLine, &argCount), ::LocalFree);
if (nullptr == args)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed parse command-line.";
::exit(-1);
}
if (3 != argCount) wchar_t *commandLine = ::GetCommandLineW();
{ int argCount = 0;
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Bad command-line length (must have exactly 2 arguments)."; std::shared_ptr<LPWSTR> args(::CommandLineToArgvW(commandLine, &argCount), ::LocalFree);
::exit(-1); if (nullptr == args)
} {
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed parse command-line.";
::exit(-1);
}
if (std::wstring(args.get()[1]) != L"-c") if (3 != argCount)
{ {
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Argument 1 must be \"-c\"."; BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Bad command-line length (must have exactly 2 arguments).";
::exit(-1); ::exit(-1);
} }
std::wstring wFilename(args.get()[2]); if (std::wstring(args.get()[1]) != L"-c")
std::string cFilename(wFilename.begin(), wFilename.end()); {
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Argument 1 must be \"-c\".";
::exit(-1);
}
boost::program_options::options_description& configOptions = UDPT::Tracker::getConfigOptions(); std::wstring wFilename(args.get()[2]);
boost::program_options::variables_map config; std::string cFilename(wFilename.begin(), wFilename.end());
boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(cFilename.c_str(), configOptions);
boost::program_options::store(parsed_options, config);
s_hServiceStatus = ::RegisterServiceCtrlHandlerEx(config["service.name"].as<std::string>().c_str(), Service::handler, NULL); boost::program_options::options_description& configOptions = UDPT::Tracker::getConfigOptions();
if (nullptr == s_hServiceStatus) boost::program_options::variables_map config;
{ boost::program_options::basic_parsed_options<wchar_t> parsed_options = boost::program_options::parse_config_file<wchar_t>(cFilename.c_str(), configOptions);
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to register service control handler."; boost::program_options::store(parsed_options, config);
::exit(-1);
}
s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; s_hServiceStatus = ::RegisterServiceCtrlHandlerEx(config["service.name"].as<std::string>().c_str(), Service::handler, NULL);
s_serviceStatus.dwServiceSpecificExitCode = 0; if (nullptr == s_hServiceStatus)
{
BOOST_LOG_SEV(logger, boost::log::trivial::fatal) << "Failed to register service control handler.";
::exit(-1);
}
reportServiceStatus(SERVICE_START_PENDING, 0, 0); s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
s_serviceStatus.dwServiceSpecificExitCode = 0;
{ reportServiceStatus(SERVICE_START_PENDING, 0, 0);
UDPT::Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(config);
reportServiceStatus(SERVICE_RUNNING, 0, 0); {
UDPT::Tracker& tracker = UDPT::Tracker::getInstance();
tracker.start(config);
tracker.wait(); reportServiceStatus(SERVICE_RUNNING, 0, 0);
reportServiceStatus(SERVICE_STOPPED, 0, 0); tracker.wait();
}
}
std::shared_ptr<void> Service::getService(DWORD access) reportServiceStatus(SERVICE_STOPPED, 0, 0);
{ }
std::shared_ptr<void> serviceManager = getServiceManager(access); }
{
SC_HANDLE service = ::OpenService(reinterpret_cast<SC_HANDLE>(serviceManager.get()), m_conf["service.name"].as<std::string>().c_str(), access);
if (nullptr == service)
{
throw OSError();
}
return std::shared_ptr<void>(service, ::CloseServiceHandle);
}
}
std::shared_ptr<void> Service::getServiceManager(DWORD access) std::shared_ptr<void> Service::getService(DWORD access)
{ {
SC_HANDLE svcMgr = ::OpenSCManager(NULL, NULL, access); std::shared_ptr<void> serviceManager = getServiceManager(access);
if (nullptr == svcMgr) {
{ SC_HANDLE service = ::OpenService(reinterpret_cast<SC_HANDLE>(serviceManager.get()), m_conf["service.name"].as<std::string>().c_str(), access);
throw OSError(); if (nullptr == service)
} {
return std::shared_ptr<void>(svcMgr, ::CloseServiceHandle); throw OSError();
} }
return std::shared_ptr<void>(service, ::CloseServiceHandle);
}
}
std::string Service::getFilename() std::shared_ptr<void> Service::getServiceManager(DWORD access)
{ {
char filename[MAX_PATH]; SC_HANDLE svcMgr = ::OpenSCManager(NULL, NULL, access);
DWORD dwRet = ::GetModuleFileName(NULL, filename, sizeof(filename) / sizeof(char)); if (nullptr == svcMgr)
if (0 == dwRet) {
{ throw OSError();
throw OSError(); }
} return std::shared_ptr<void>(svcMgr, ::CloseServiceHandle);
return std::string(filename); }
}
std::string Service::getFilename()
{
char filename[MAX_PATH];
DWORD dwRet = ::GetModuleFileName(NULL, filename, sizeof(filename) / sizeof(char));
if (0 == dwRet)
{
throw OSError();
}
return std::string(filename);
}
} }
#endif #endif

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -28,42 +28,42 @@
#ifdef WIN32 #ifdef WIN32
namespace UDPT namespace UDPT
{ {
class Service class Service
{ {
public: public:
Service(const boost::program_options::variables_map& conf); Service(const boost::program_options::variables_map& conf);
virtual ~Service(); virtual ~Service();
void install(const std::string& config_path); void install(const std::string& config_path);
void uninstall(); void uninstall();
void start(); void start();
void stop(); void stop();
void setup(); void setup();
private: private:
const boost::program_options::variables_map& m_conf; const boost::program_options::variables_map& m_conf;
static SERVICE_STATUS_HANDLE s_hServiceStatus; static SERVICE_STATUS_HANDLE s_hServiceStatus;
static SERVICE_STATUS s_serviceStatus; static SERVICE_STATUS s_serviceStatus;
std::shared_ptr<void> getService(DWORD access); std::shared_ptr<void> getService(DWORD access);
static DWORD WINAPI handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context); static DWORD WINAPI handler(DWORD controlCode, DWORD dwEventType, LPVOID eventData, LPVOID context);
static void reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint); static void reportServiceStatus(DWORD currentState, DWORD dwExitCode, DWORD dwWaitHint);
static VOID WINAPI serviceMain(DWORD argc, LPCSTR argv[]); static VOID WINAPI serviceMain(DWORD argc, LPCSTR argv[]);
static std::shared_ptr<void> getServiceManager(DWORD access); static std::shared_ptr<void> getServiceManager(DWORD access);
static std::string getFilename(); static std::string getFilename();
}; };
} }
#endif #endif

View file

@ -1,65 +1,65 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "tools.h" #include "tools.h"
#include "multiplatform.h" #include "multiplatform.h"
void m_byteswap (void *dest, void *src, int sz) void m_byteswap (void *dest, void *src, int sz)
{ {
int i; int i;
for (i = 0;i < sz;i++) for (i = 0;i < sz;i++)
{ {
((char*)dest)[i] = ((char*)src)[(sz - 1) - i]; ((char*)dest)[i] = ((char*)src)[(sz - 1) - i];
} }
} }
uint16_t m_hton16(uint16_t n) uint16_t m_hton16(uint16_t n)
{ {
uint16_t r; uint16_t r;
m_byteswap (&r, &n, 2); m_byteswap (&r, &n, 2);
return r; return r;
} }
uint64_t m_hton64 (uint64_t n) uint64_t m_hton64 (uint64_t n)
{ {
uint64_t r; uint64_t r;
m_byteswap (&r, &n, 8); m_byteswap (&r, &n, 8);
return r; return r;
} }
uint32_t m_hton32 (uint32_t n) uint32_t m_hton32 (uint32_t n)
{ {
uint64_t r; uint64_t r;
m_byteswap (&r, &n, 4); m_byteswap (&r, &n, 4);
return r; return r;
} }
static const char hexadecimal[] = "0123456789abcdef"; static const char hexadecimal[] = "0123456789abcdef";
void to_hex_str (const uint8_t *hash, char *data) void to_hex_str (const uint8_t *hash, char *data)
{ {
int i; int i;
for (i = 0;i < 20;i++) for (i = 0;i < 20;i++)
{ {
data[i * 2] = hexadecimal[hash[i] / 16]; data[i * 2] = hexadecimal[hash[i] / 16];
data[i * 2 + 1] = hexadecimal[hash[i] % 16]; data[i * 2 + 1] = hexadecimal[hash[i] % 16];
} }
data[40] = '\0'; data[40] = '\0';
} }

View file

@ -1,50 +1,50 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef TOOLS_H_ #ifndef TOOLS_H_
#define TOOLS_H_ #define TOOLS_H_
#include <stdint.h> #include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** /**
* Swaps Bytes: * Swaps Bytes:
* example (htons): * example (htons):
* short a = 1234; * short a = 1234;
* short b; * short b;
* m_byteswap (&b, &a, sizeof(a)); * m_byteswap (&b, &a, sizeof(a));
*/ */
void m_byteswap (void *dest, void *src, int sz); void m_byteswap (void *dest, void *src, int sz);
uint16_t m_hton16(uint16_t n); uint16_t m_hton16(uint16_t n);
uint32_t m_hton32 (uint32_t n); uint32_t m_hton32 (uint32_t n);
uint64_t m_hton64 (uint64_t n); uint64_t m_hton64 (uint64_t n);
void to_hex_str (const uint8_t *hash, char *data); void to_hex_str (const uint8_t *hash, char *data);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* TOOLS_H_ */ #endif /* TOOLS_H_ */

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -16,52 +16,60 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <iostream>
#include <fstream>
#include "tracker.hpp" #include "tracker.hpp"
#include "logging.hpp"
namespace UDPT namespace UDPT
{ {
Tracker::Tracker() : m_logger(boost::log::keywords::channel = "TRACKER") Tracker::Tracker() : m_logStream(nullptr)
{ {
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Initialized Tracker";
} }
Tracker::~Tracker() Tracker::~Tracker()
{ {
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Destroying Tracker..."; try {
if (nullptr != m_logStream) {
m_logStream->flush();
delete m_logStream;
m_logStream = nullptr;
}
} catch (...) {
}
} }
void Tracker::stop() void Tracker::stop()
{ {
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Requesting Tracker to terminate."; LOG_INFO("tracker", "Requesting components to terminate...");
m_udpTracker->stop(); m_udpTracker->stop();
wait();
// cause other components to destruct.
m_apiSrv = nullptr; m_apiSrv = nullptr;
m_webApp = nullptr; m_webApp = nullptr;
m_udpTracker = nullptr;
} }
void Tracker::wait() void Tracker::wait()
{ {
m_udpTracker->wait(); m_udpTracker->wait();
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Tracker terminated.";
} }
void Tracker::start(const boost::program_options::variables_map& conf) void Tracker::start(const boost::program_options::variables_map& conf)
{ {
BOOST_LOG_SEV(m_logger, boost::log::trivial::debug) << "Starting Tracker..."; setupLogging(conf);
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf)); LOG_INFO("core", "Initializing...");
m_udpTracker = std::shared_ptr<UDPTracker>(new UDPTracker(conf));
if (conf["apiserver.enable"].as<bool>()) if (conf["apiserver.enable"].as<bool>())
{ {
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Initializing and deploying WebAPI...";
m_apiSrv = std::shared_ptr<UDPT::Server::HTTPServer>(new UDPT::Server::HTTPServer(conf)); m_apiSrv = std::shared_ptr<UDPT::Server::HTTPServer>(new UDPT::Server::HTTPServer(conf));
m_webApp = std::shared_ptr<UDPT::Server::WebApp>(new UDPT::Server::WebApp(m_apiSrv, m_udpTracker->m_conn.get(), conf)); m_webApp = std::shared_ptr<UDPT::Server::WebApp>(new UDPT::Server::WebApp(m_apiSrv, m_udpTracker->m_conn.get(), conf));
m_webApp->deploy(); m_webApp->deploy();
} }
m_udpTracker->start(); m_udpTracker->start();
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "Tracker started.";
} }
Tracker& Tracker::getInstance() Tracker& Tracker::getInstance()
@ -71,75 +79,73 @@ namespace UDPT
return s_tracker; return s_tracker;
} }
boost::program_options::options_description Tracker::getConfigOptions() boost::program_options::options_description Tracker::getConfigOptions()
{ {
boost::program_options::options_description configOptions("Configuration options"); boost::program_options::options_description configOptions("Configuration options");
configOptions.add_options() configOptions.add_options()
("db.driver", boost::program_options::value<std::string>()->default_value("sqlite3"), "database driver to use") ("db.driver", boost::program_options::value<std::string>()->default_value("sqlite3"), "database driver to use")
("db.param", boost::program_options::value<std::string>()->default_value("/var/lib/udpt.db"), "database connection parameters") ("db.param", boost::program_options::value<std::string>()->default_value("/var/lib/udpt.db"), "database connection parameters")
("tracker.is_dynamic", boost::program_options::value<bool>()->default_value(true), "Sets if the tracker is dynamic") ("tracker.is_dynamic", boost::program_options::value<bool>()->default_value(true), "Sets if the tracker is dynamic")
("tracker.port", boost::program_options::value<unsigned short>()->default_value(6969), "UDP port to listen on") ("tracker.port", boost::program_options::value<unsigned short>()->default_value(6969), "UDP port to listen on")
("tracker.threads", boost::program_options::value<unsigned>()->default_value(5), "threads to run (UDP only)") ("tracker.threads", boost::program_options::value<unsigned>()->default_value(5), "threads to run (UDP only)")
("tracker.allow_remotes", boost::program_options::value<bool>()->default_value(true), "allows clients to report remote IPs") ("tracker.allow_remotes", boost::program_options::value<bool>()->default_value(true), "allows clients to report remote IPs")
("tracker.allow_iana_ips", boost::program_options::value<bool>()->default_value(false), "allows IANA reserved IPs to connect (useful for debugging)") ("tracker.allow_iana_ips", boost::program_options::value<bool>()->default_value(false), "allows IANA reserved IPs to connect (useful for debugging)")
("tracker.announce_interval", boost::program_options::value<unsigned>()->default_value(1800), "announce interval") ("tracker.announce_interval", boost::program_options::value<unsigned>()->default_value(1800), "announce interval")
("tracker.cleanup_interval", boost::program_options::value<unsigned>()->default_value(120), "sets database cleanup interval") ("tracker.cleanup_interval", boost::program_options::value<unsigned>()->default_value(120), "sets database cleanup interval")
("apiserver.enable", boost::program_options::value<bool>()->default_value(0), "Enable API server?") ("apiserver.enable", boost::program_options::value<bool>()->default_value(0), "Enable API server?")
("apiserver.threads", boost::program_options::value<unsigned short>()->default_value(1), "threads for API server") ("apiserver.threads", boost::program_options::value<unsigned short>()->default_value(1), "threads for API server")
("apiserver.port", boost::program_options::value<unsigned short>()->default_value(6969), "TCP port to listen on") ("apiserver.port", boost::program_options::value<unsigned short>()->default_value(6969), "TCP port to listen on")
("logging.filename", boost::program_options::value<std::string>()->default_value("/var/log/udpt.log"), "file to write logs to") ("logging.filename", boost::program_options::value<std::string>()->default_value("/var/log/udpt.log"), "file to write logs to")
("logging.level", boost::program_options::value<std::string>()->default_value("warning"), "log level (fatal/error/warning/info/debug/trace)") ("logging.level", boost::program_options::value<std::string>()->default_value("warning"), "log level (fatal/error/warning/info/debug)")
#ifdef linux #ifdef linux
("daemon.chdir", boost::program_options::value<std::string>()->default_value("/"), "home directory for daemon") ("daemon.chdir", boost::program_options::value<std::string>()->default_value("/"), "home directory for daemon")
#endif #endif
#ifdef WIN32 #ifdef WIN32
("service.name", boost::program_options::value<std::string>()->default_value("udpt"), "service name to use") ("service.name", boost::program_options::value<std::string>()->default_value("udpt"), "service name to use")
#endif #endif
; ;
return configOptions; return configOptions;
} }
void Tracker::setupLogging(const boost::program_options::variables_map& config, boost::log::sources::severity_channel_logger_mt<>& logger) void Tracker::setupLogging(const boost::program_options::variables_map& va_map) {
{ Logging::Logger& logger = Logging::Logger::getLogger();
boost::log::add_common_attributes();
boost::shared_ptr<boost::log::sinks::text_file_backend> logBackend = boost::make_shared<boost::log::sinks::text_file_backend>(
boost::log::keywords::file_name = config["logging.filename"].as<std::string>(),
boost::log::keywords::auto_flush = true,
boost::log::keywords::open_mode = std::ios::out | std::ios::app
);
typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::text_file_backend> udptSink_t;
boost::shared_ptr<udptSink_t> async_sink(new udptSink_t(logBackend));
async_sink->set_formatter(
boost::log::expressions::stream
<< boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S") << " "
<< boost::log::expressions::attr<int>("Severity")
<< " [" << boost::log::expressions::attr<std::string>("Channel") << "] \t"
<< boost::log::expressions::smessage
);
auto loggingCore = boost::log::core::get();
loggingCore->add_sink(async_sink);
std::string severity = config["logging.level"].as<std::string>(); logger.addStream(&std::cerr, Logging::Severity::FATAL);
std::transform(severity.begin(), severity.end(), severity.begin(), ::tolower);
int severityVal = boost::log::trivial::warning;
if ("fatal" == severity) severityVal = boost::log::trivial::fatal;
else if ("error" == severity) severityVal = boost::log::trivial::error;
else if ("warning" == severity) severityVal = boost::log::trivial::warning;
else if ("info" == severity) severityVal = boost::log::trivial::info;
else if ("debug" == severity) severityVal = boost::log::trivial::debug;
else if ("trace" == severity) severityVal = boost::log::trivial::trace;
else
{
BOOST_LOG_SEV(logger, boost::log::trivial::warning) << "Unknown debug level \"" << severity << "\" defaulting to warning";
}
loggingCore->set_filter( Logging::Severity severity;
boost::log::trivial::severity >= severityVal std::string severity_text = va_map["logging.level"].as<std::string>();
); std::transform(severity_text.begin(), severity_text.end(), severity_text.begin(), ::tolower);
}
if (severity_text == "fatal") {
severity = Logging::Severity::FATAL;
} else if (severity_text == "error") {
severity = Logging::Severity::ERROR;
} else if (severity_text == "warning") {
severity = Logging::Severity::WARNING;
} else if (severity_text == "info") {
severity = Logging::Severity::INFO;
} else if (severity_text == "debug") {
severity = Logging::Severity::DEBUG;
} else {
severity = Logging::Severity::UNSET;
}
const std::string& logFileName = va_map["logging.filename"].as<std::string>();
Logging::Severity real_severity = (severity == Logging::Severity::UNSET ? Logging::Severity::INFO : severity);
if (logFileName.length() == 0 || logFileName == "--") {
logger.addStream(&std::cerr, real_severity);
} else {
m_logStream = new ofstream(logFileName, std::ios::app | std::ios::out);
logger.addStream(m_logStream, real_severity);
}
if (severity != real_severity) {
LOG_WARN("core", "'" << severity_text << "' is invalid, log level set to " << real_severity);
}
}
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
@ -21,16 +21,7 @@
#include <memory> #include <memory>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/async_frontend.hpp>
#include <boost/log/keywords/format.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include "multiplatform.h" #include "multiplatform.h"
#include "udpTracker.hpp" #include "udpTracker.hpp"
@ -51,18 +42,18 @@ namespace UDPT
void wait(); void wait();
static Tracker& getInstance(); static Tracker& getInstance();
static boost::program_options::options_description getConfigOptions(); static boost::program_options::options_description getConfigOptions();
static void setupLogging(const boost::program_options::variables_map& config, boost::log::sources::severity_channel_logger_mt<>& logger);
private: private:
std::shared_ptr<UDPT::UDPTracker> m_udpTracker; std::shared_ptr<UDPT::UDPTracker> m_udpTracker;
std::shared_ptr<UDPT::Server::HTTPServer> m_apiSrv; std::shared_ptr<UDPT::Server::HTTPServer> m_apiSrv;
std::shared_ptr<UDPT::Server::WebApp> m_webApp; std::shared_ptr<UDPT::Server::WebApp> m_webApp;
boost::log::sources::severity_channel_logger_mt<> m_logger;
Tracker(); Tracker();
void setupLogging(const boost::program_options::variables_map& va_map);
std::ostream *m_logStream;
}; };
} }

View file

@ -1,477 +1,450 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "udpTracker.hpp" #include "udpTracker.hpp"
#include "logging.hpp"
using namespace UDPT::Data;
using namespace UDPT::Data;
#define UDP_BUFFER_SIZE 2048
#define UDP_BUFFER_SIZE 2048
namespace UDPT
{ namespace UDPT
UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf), m_logger(boost::log::keywords::channel = "UDPTracker") {
{ UDPTracker::UDPTracker(const boost::program_options::variables_map& conf) : m_conf(conf) {
this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>(); this->m_allowRemotes = conf["tracker.allow_remotes"].as<bool>();
this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>(); this->m_allowIANA_IPs = conf["tracker.allow_iana_ips"].as<bool>();
this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>(); this->m_isDynamic = conf["tracker.is_dynamic"].as<bool>();
this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>(); this->m_announceInterval = conf["tracker.announce_interval"].as<unsigned>();
this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>(); this->m_cleanupInterval = conf["tracker.cleanup_interval"].as<unsigned>();
this->m_port = conf["tracker.port"].as<unsigned short>(); this->m_port = conf["tracker.port"].as<unsigned short>();
this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1; this->m_threadCount = conf["tracker.threads"].as<unsigned>() + 1;
std::list<SOCKADDR_IN> addrs; this->m_localEndpoint.sin_family = AF_INET;
this->m_localEndpoint.sin_port = m_hton16(m_port);
if (addrs.empty()) this->m_localEndpoint.sin_addr.s_addr = 0L;
{
SOCKADDR_IN sa; this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic));
sa.sin_port = m_hton16(m_port); }
sa.sin_addr.s_addr = 0L;
addrs.push_back(sa); UDPTracker::~UDPTracker() {
} }
this->m_localEndpoint = addrs.front(); void UDPTracker::start() {
SOCKET sock;
this->m_conn = std::shared_ptr<DatabaseDriver>(new Data::SQLite3Driver(m_conf, this->m_isDynamic)); int r, // saves results
} i, // loop index
yup; // just to set TRUE
UDPTracker::~UDPTracker() std::string dbname;// saves the Database name.
{
stop(); sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
} if (sock == INVALID_SOCKET)
{
void UDPTracker::start() LOG_FATAL("udp-tracker", "Failed to create socket. error=" << errno);
{ throw UDPT::UDPTException("Failed to create socket");
SOCKET sock; }
int r, // saves results
i, // loop index yup = 1;
yup; // just to set TRUE ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1);
std::string dbname;// saves the Database name.
{
sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // don't block recvfrom for too long.
if (sock == INVALID_SOCKET) #if defined(linux)
{ timeval timeout = { 0 };
throw UDPT::UDPTException("Failed to create socket"); timeout.tv_sec = 5;
} #elif defined(WIN32)
DWORD timeout = 5000;
yup = 1; #else
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yup, 1); #error Unsupported OS.
#endif
{ ::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
// don't block recvfrom for too long. }
#if defined(linux)
timeval timeout = { 0 }; this->m_localEndpoint.sin_family = AF_INET;
timeout.tv_sec = 5; r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN));
#elif defined(WIN32)
DWORD timeout = 5000; if (r == SOCKET_ERROR)
#else {
#error Unsupported OS. LOG_FATAL("udp-tracker", "Failed to bind socket. error=" << errno);
#endif
::setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout)); #ifdef WIN32
} ::closesocket(sock);
#elif defined (linux)
this->m_localEndpoint.sin_family = AF_INET; ::close(sock);
r = ::bind(sock, reinterpret_cast<SOCKADDR*>(&this->m_localEndpoint), sizeof(SOCKADDR_IN)); #endif
throw UDPT::UDPTException("Failed to bind socket.");
if (r == SOCKET_ERROR) }
{
#ifdef WIN32 this->m_sock = sock;
::closesocket(sock);
#elif defined (linux) LOG_INFO("udp-tracker", "Tracker bound to " << inet_ntoa(this->m_localEndpoint.sin_addr));
::close(sock);
#endif // create maintainer thread.
throw UDPT::UDPTException("Failed to bind socket."); m_threads.push_back(std::thread(UDPTracker::_maintainance_start, this));
} LOG_INFO("udp-tracker", "Started maintenance thread @" << m_threads.back().get_id());
{ m_shouldRun = true;
char buff[INET_ADDRSTRLEN]; for (i = 1;i < this->m_threadCount; i++)
BOOST_LOG_SEV(m_logger, boost::log::trivial::info) << "UDP tracker bound on " << ::inet_ntop(AF_INET, reinterpret_cast<LPVOID>(&m_localEndpoint.sin_addr), buff, sizeof(buff)) << ":" << htons(m_localEndpoint.sin_port); {
} m_threads.push_back(std::thread(UDPTracker::_thread_start, this));
LOG_INFO("udp-tracker", "Started worker thread @" << m_threads.back().get_id());
this->m_sock = sock; }
}
// create maintainer thread.
void UDPTracker::stop() {
m_threads.push_back(boost::thread(UDPTracker::_maintainance_start, this)); // tell workers to stop running...
m_shouldRun = false;
for (i = 1;i < this->m_threadCount; i++)
{ // stop maintenance thread's sleep...
m_threads.push_back(boost::thread(UDPTracker::_thread_start, this)); m_maintenanceCondition.notify_one();
} }
}
/**
void UDPTracker::stop() * @brief blocks until all threads die.
{ * @note This method should be called only once, preferably by the main thread.
#ifdef linux * **/
::close(m_sock); void UDPTracker::wait() {
#elif defined (WIN32) LOG_INFO("udp-tracker", "Waiting for threads to terminate...");
::closesocket(m_sock);
#endif for (std::vector<std::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it)
{
BOOST_LOG_SEV(m_logger, boost::log::trivial::warning) << "Interrupting workers..."; it->join();
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it) }
{
it->interrupt(); LOG_INFO("udp-tracker", "UDP Tracker terminated");
}
#ifdef linux
wait(); ::close(m_sock);
} #elif defined (WIN32)
::closesocket(m_sock);
void UDPTracker::wait() #endif
{ }
BOOST_LOG_SEV(m_logger, boost::log::trivial::warning) << "Waiting for threads to terminate...";
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg) {
for (std::vector<boost::thread>::iterator it = m_threads.begin(); it != m_threads.end(); ++it) struct udp_error_response error;
{ int msg_sz, // message size to send.
it->join(); i; // copy loop
} char buff [1024]; // more than reasonable message size...
}
error.action = m_hton32 (3);
int UDPTracker::sendError(UDPTracker* usi, SOCKADDR_IN* remote, uint32_t transactionID, const std::string &msg) error.transaction_id = transactionID;
{ error.message = (char*)msg.c_str();
struct udp_error_response error;
int msg_sz, // message size to send. msg_sz = 4 + 4 + 1 + msg.length();
i; // copy loop
char buff [1024]; // more than reasonable message size... // test against overflow message. resolves issue 4.
if (msg_sz > 1024)
error.action = m_hton32 (3); return -1;
error.transaction_id = transactionID;
error.message = (char*)msg.c_str(); ::memcpy(buff, &error, 8);
for (i = 8;i <= msg_sz;i++)
msg_sz = 4 + 4 + 1 + msg.length(); {
buff[i] = msg[i - 8];
// test against overflow message. resolves issue 4. }
if (msg_sz > 1024)
return -1; ::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote));
::memcpy(buff, &error, 8); LOG_DEBUG("udp-tracker", "Error sent to " << inet_ntoa(remote->sin_addr) << ", '" << msg << "' (len=" << msg_sz << ")");
for (i = 8;i <= msg_sz;i++)
{ return 0;
buff[i] = msg[i - 8]; }
}
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data) {
::sendto(usi->m_sock, buff, msg_sz, 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(*remote)); ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data);
ConnectionResponse resp;
return 0;
} resp.action = m_hton32(0);
resp.transaction_id = req->transaction_id;
int UDPTracker::handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data)
{ if (!usi->m_conn->genConnectionId(&resp.connection_id,
ConnectionRequest *req = reinterpret_cast<ConnectionRequest*>(data); m_hton32(remote->sin_addr.s_addr),
ConnectionResponse resp; m_hton16(remote->sin_port)))
{
resp.action = m_hton32(0); return 1;
resp.transaction_id = req->transaction_id; }
if (!usi->m_conn->genConnectionId(&resp.connection_id, ::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port))) return 0;
{ }
return 1;
} int UDPTracker::handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data) {
AnnounceRequest *req;
::sendto(usi->m_sock, (char*)&resp, sizeof(ConnectionResponse), 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN)); AnnounceResponse *resp;
int q, // peer counts
{ bSize, // message size
char buffer[INET_ADDRSTRLEN]; i; // loop index
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::debug) << "Connection Request from " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer)) << "; cId=" << resp.connection_id << "; tId=" << resp.transaction_id; DatabaseDriver::TorrentEntry tE;
}
uint8_t buff[1028]; // Reasonable buffer size. (header+168 peers)
return 0; req = (AnnounceRequest*)data;
}
if (!usi->m_conn->verifyConnectionId(req->connection_id,
int UDPTracker::handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data) m_hton32(remote->sin_addr.s_addr),
{ m_hton16(remote->sin_port)))
AnnounceRequest *req; {
AnnounceResponse *resp; return 1;
int q, // peer counts }
bSize, // message size
i; // loop index // change byte order:
DatabaseDriver::TorrentEntry tE; req->port = m_hton16(req->port);
req->ip_address = m_hton32(req->ip_address);
uint8_t buff[1028]; // Reasonable buffer size. (header+168 peers) req->downloaded = m_hton64(req->downloaded);
req->event = m_hton32(req->event); // doesn't really matter for this tracker
req = (AnnounceRequest*)data; req->uploaded = m_hton64(req->uploaded);
req->num_want = m_hton32(req->num_want);
if (!usi->m_conn->verifyConnectionId(req->connection_id, req->left = m_hton64(req->left);
m_hton32(remote->sin_addr.s_addr),
m_hton16(remote->sin_port))) if (!usi->m_allowRemotes && req->ip_address != 0)
{ {
return 1; UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
} return 0;
}
// change byte order:
req->port = m_hton16(req->port); if (!usi->m_conn->isTorrentAllowed(req->info_hash))
req->ip_address = m_hton32(req->ip_address); {
req->downloaded = m_hton64(req->downloaded); UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
req->event = m_hton32(req->event); // doesn't really matter for this tracker return 0;
req->uploaded = m_hton64(req->uploaded); }
req->num_want = m_hton32(req->num_want);
req->left = m_hton64(req->left); // load peers
q = 30;
if (!usi->m_allowRemotes && req->ip_address != 0) if (req->num_want >= 1)
{ q = std::min<int>(q, req->num_want);
UDPTracker::sendError(usi, remote, req->transaction_id, "Tracker doesn't allow remote IP's; Request ignored.");
return 0; DatabaseDriver::TrackerEvents event;
}
{
if (!usi->m_conn->isTorrentAllowed(req->info_hash)) std::shared_ptr<DatabaseDriver::PeerEntry> peersSptr = std::shared_ptr<DatabaseDriver::PeerEntry>(new DatabaseDriver::PeerEntry[q]);
{ DatabaseDriver::PeerEntry *peers = peersSptr.get();
UDPTracker::sendError(usi, remote, req->transaction_id, "info_hash not registered.");
return 0; switch (req->event)
} {
case 1:
// load peers event = DatabaseDriver::EVENT_COMPLETE;
q = 30; break;
if (req->num_want >= 1) case 2:
q = std::min<int>(q, req->num_want); event = DatabaseDriver::EVENT_START;
break;
DatabaseDriver::TrackerEvents event; case 3:
event = DatabaseDriver::EVENT_STOP;
{ break;
std::shared_ptr<DatabaseDriver::PeerEntry> peersSptr = std::shared_ptr<DatabaseDriver::PeerEntry>(new DatabaseDriver::PeerEntry[q]); default:
DatabaseDriver::PeerEntry *peers = peersSptr.get(); event = DatabaseDriver::EVENT_UNSPEC;
break;
switch (req->event) }
{
case 1: if (event == DatabaseDriver::EVENT_STOP)
event = DatabaseDriver::EVENT_COMPLETE; q = 0; // no need for peers when stopping.
break;
case 2: if (q > 0)
event = DatabaseDriver::EVENT_START; usi->m_conn->getPeers(req->info_hash, &q, peers);
break;
case 3: bSize = 20; // header is 20 bytes
event = DatabaseDriver::EVENT_STOP; bSize += (6 * q); // + 6 bytes per peer.
break;
default: tE.info_hash = req->info_hash;
event = DatabaseDriver::EVENT_UNSPEC; usi->m_conn->getTorrentInfo(&tE);
break;
} resp = (AnnounceResponse*)buff;
resp->action = m_hton32(1);
if (event == DatabaseDriver::EVENT_STOP) resp->interval = m_hton32(usi->m_announceInterval);
q = 0; // no need for peers when stopping. resp->leechers = m_hton32(tE.leechers);
resp->seeders = m_hton32(tE.seeders);
if (q > 0) resp->transaction_id = req->transaction_id;
usi->m_conn->getPeers(req->info_hash, &q, peers);
for (i = 0; i < q; i++)
bSize = 20; // header is 20 bytes {
bSize += (6 * q); // + 6 bytes per peer. int x = i * 6;
// network byte order!!!
tE.info_hash = req->info_hash;
usi->m_conn->getTorrentInfo(&tE); // IP
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24);
resp = (AnnounceResponse*)buff; buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16);
resp->action = m_hton32(1); buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8);
resp->interval = m_hton32(usi->m_announceInterval); buff[23 + x] = (peers[i].ip & 0xff);
resp->leechers = m_hton32(tE.leechers);
resp->seeders = m_hton32(tE.seeders); // port
resp->transaction_id = req->transaction_id; buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8);
buff[25 + x] = (peers[i].port & 0xff);
for (i = 0; i < q; i++)
{ }
int x = i * 6; }
// network byte order!!! ::sendto(usi->m_sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
LOG_DEBUG("udp-tracker", "Announce request from " << inet_ntoa(remote->sin_addr) << " (event=" << event << "), Sent " << q << " peers");
// IP
buff[20 + x] = ((peers[i].ip & (0xff << 24)) >> 24); // update DB.
buff[21 + x] = ((peers[i].ip & (0xff << 16)) >> 16); uint32_t ip;
buff[22 + x] = ((peers[i].ip & (0xff << 8)) >> 8); if (req->ip_address == 0) // default
buff[23 + x] = (peers[i].ip & 0xff); ip = m_hton32 (remote->sin_addr.s_addr);
else
// port ip = req->ip_address;
buff[24 + x] = ((peers[i].port & (0xff << 8)) >> 8); usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port,
buff[25 + x] = (peers[i].port & 0xff); req->downloaded, req->left, req->uploaded, event);
} return 0;
} }
::sendto(usi->m_sock, (char*)buff, bSize, 0, (SOCKADDR*)remote, sizeof(SOCKADDR_IN));
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len) {
// update DB. ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data);
uint32_t ip; int v, // validation helper
if (req->ip_address == 0) // default c, // torrent counter
ip = m_hton32 (remote->sin_addr.s_addr); i, // loop counter
else j; // loop counter
ip = req->ip_address; uint8_t hash [20];
usi->m_conn->updatePeer(req->peer_id, req->info_hash, ip, req->port, ScrapeResponse *resp;
req->downloaded, req->left, req->uploaded, event); uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024
return 0; // validate request length:
} v = len - 16;
if (v < 0 || v % 20 != 0)
int UDPTracker::handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len) {
{ UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
ScrapeRequest *sR = reinterpret_cast<ScrapeRequest*>(data); return 0;
int v, // validation helper }
c, // torrent counter
i, // loop counter if (!usi->m_conn->verifyConnectionId(sR->connection_id,
j; // loop counter m_hton32(remote->sin_addr.s_addr),
uint8_t hash [20]; m_hton16(remote->sin_port)))
ScrapeResponse *resp; {
uint8_t buffer [1024]; // up to 74 torrents can be scraped at once (17*74+8) < 1024 LOG_DEBUG("udp-tracker", "Bad connection id from " << inet_ntoa(remote->sin_addr));
return 1;
// validate request length: }
v = len - 16;
if (v < 0 || v % 20 != 0) // get torrent count.
{ c = v / 20;
UDPTracker::sendError(usi, remote, sR->transaction_id, "Bad scrape request.");
return 0; resp = reinterpret_cast<ScrapeResponse*>(buffer);
} resp->action = m_hton32(2);
resp->transaction_id = sR->transaction_id;
if (!usi->m_conn->verifyConnectionId(sR->connection_id,
m_hton32(remote->sin_addr.s_addr), for (i = 0;i < c;i++)
m_hton16(remote->sin_port))) {
{ int32_t *seeders,
return 1; *completed,
} *leechers;
// get torrent count. for (j = 0; j < 20;j++)
c = v / 20; hash[j] = data[j + (i*20)+16];
resp = reinterpret_cast<ScrapeResponse*>(buffer); seeders = (int32_t*)&buffer[i*12+8];
resp->action = m_hton32(2); completed = (int32_t*)&buffer[i*12+12];
resp->transaction_id = sR->transaction_id; leechers = (int32_t*)&buffer[i*12+16];
for (i = 0;i < c;i++) DatabaseDriver::TorrentEntry tE;
{ tE.info_hash = hash;
int32_t *seeders, if (!usi->m_conn->getTorrentInfo(&tE))
*completed, {
*leechers; sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data");
return 0;
for (j = 0; j < 20;j++) }
hash[j] = data[j + (i*20)+16];
*seeders = m_hton32(tE.seeders);
seeders = (int32_t*)&buffer[i*12+8]; *completed = m_hton32(tE.completed);
completed = (int32_t*)&buffer[i*12+12]; *leechers = m_hton32(tE.leechers);
leechers = (int32_t*)&buffer[i*12+16]; }
DatabaseDriver::TorrentEntry tE; ::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN));
tE.info_hash = hash; LOG_DEBUG("udp-tracker", "Scrape request from " << inet_ntoa(remote->sin_addr) << ", Sent " << c << " torrents");
if (!usi->m_conn->getTorrentInfo(&tE))
{ return 0;
sendError(usi, remote, sR->transaction_id, "Scrape Failed: couldn't retrieve torrent data"); }
return 0;
} int UDPTracker::isIANAIP(uint32_t ip) {
uint8_t x = (ip % 256);
*seeders = m_hton32(tE.seeders); if (x == 0 || x == 10 || x == 127 || x >= 224)
*completed = m_hton32(tE.completed); return 1;
*leechers = m_hton32(tE.leechers); return 0;
} }
::sendto(usi->m_sock, reinterpret_cast<const char*>(buffer), sizeof(buffer), 0, reinterpret_cast<SOCKADDR*>(remote), sizeof(SOCKADDR_IN)); int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r) {
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data);
return 0; uint32_t action;
}
action = m_hton32(cR->action);
int UDPTracker::isIANAIP(uint32_t ip)
{ if (!usi->m_allowIANA_IPs)
uint8_t x = (ip % 256); {
if (x == 0 || x == 10 || x == 127 || x >= 224) if (isIANAIP(remote->sin_addr.s_addr))
return 1; {
return 0; LOG_DEBUG("udp-tracker", "Request from IANA reserved IP rejected (" << inet_ntoa(remote->sin_addr) << ")");
} return 0; // Access Denied: IANA reserved IP.
}
int UDPTracker::resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r) }
{
ConnectionRequest* cR = reinterpret_cast<ConnectionRequest*>(data); if (action == 0 && r >= 16)
uint32_t action; return UDPTracker::handleConnection(usi, remote, data);
else if (action == 1 && r >= 98)
action = m_hton32(cR->action); return UDPTracker::handleAnnounce(usi, remote, data);
else if (action == 2)
if (!usi->m_allowIANA_IPs) return UDPTracker::handleScrape(usi, remote, data, r);
{ else
if (isIANAIP(remote->sin_addr.s_addr)) {
{ UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request.");
char buffer[INET_ADDRSTRLEN]; return -1;
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::warning) << "Client ignored (IANA IP): " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer)); }
return 0; // Access Denied: IANA reserved IP. }
}
} void UDPTracker::_thread_start(UDPTracker *usi) {
SOCKADDR_IN remoteAddr;
{ char tmpBuff[UDP_BUFFER_SIZE];
char buffer[INET_ADDRSTRLEN]; #ifdef linux
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::debug) << "Client request from " << ::inet_ntop(AF_INET, &remote->sin_addr, buffer, sizeof(buffer)); socklen_t addrSz;
} #else
int addrSz;
if (action == 0 && r >= 16) #endif
return UDPTracker::handleConnection(usi, remote, data);
else if (action == 1 && r >= 98) addrSz = sizeof(SOCKADDR_IN);
return UDPTracker::handleAnnounce(usi, remote, data);
else if (action == 2) while (usi->m_shouldRun)
return UDPTracker::handleScrape(usi, remote, data, r); {
else // peek into the first 12 bytes of data; determine if connection request or announce request.
{ int r = ::recvfrom(usi->m_sock, (char*)tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, &addrSz);
UDPTracker::sendError(usi, remote, cR->transaction_id, "Tracker couldn't understand Client's request."); if (r <= 0)
return -1; {
} std::this_thread::yield();
continue;
return 0; }
}
UDPTracker::resolveRequest(usi, &remoteAddr, tmpBuff, r);
void UDPTracker::_thread_start(UDPTracker *usi) }
{
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::info) << "Worker thread started with PID=" << boost::this_thread::get_id() << "."; LOG_INFO("udp-tracker", "worker " << std::this_thread::get_id() << " exited.");
SOCKADDR_IN remoteAddr; }
char tmpBuff[UDP_BUFFER_SIZE];
void UDPTracker::_maintainance_start(UDPTracker* usi) {
#ifdef linux std::unique_lock<std::mutex> lk (usi->m_maintenanceMutex);
socklen_t addrSz;
#else while (true)
int addrSz; {
#endif if (std::cv_status::no_timeout == usi->m_maintenanceCondition.wait_for(lk, std::chrono::seconds(usi->m_cleanupInterval))) {
break;
addrSz = sizeof(SOCKADDR_IN); }
LOG_INFO("udp-tracker", "Maintenance running...");
while (true) usi->m_conn->cleanup();
{ }
// peek into the first 12 bytes of data; determine if connection request or announce request.
int r = ::recvfrom(usi->m_sock, (char*)tmpBuff, UDP_BUFFER_SIZE, 0, (SOCKADDR*)&remoteAddr, &addrSz); lk.unlock();
if (r <= 0) LOG_INFO("udp-tracker", "Maintenance thread " << std::this_thread::get_id() << " existed.");
{ }
boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); };
continue;
}
{
boost::this_thread::disable_interruption di;
UDPTracker::resolveRequest(usi, &remoteAddr, tmpBuff, r);
}
}
}
void UDPTracker::_maintainance_start(UDPTracker* usi)
{
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::info) << "Maintenance thread started with PID=" << boost::this_thread::get_id() << ".";
while (true)
{
{
boost::this_thread::disable_interruption di;
BOOST_LOG_SEV(usi->m_logger, boost::log::trivial::info) << "Running cleanup...";
usi->m_conn->cleanup();
}
boost::this_thread::sleep_for(boost::chrono::seconds(usi->m_cleanupInterval));
}
}
};

View file

@ -1,185 +1,188 @@
/* /*
* Copyright © 2012-2016 Naim A. * Copyright © 2012-2017 Naim A.
* *
* This file is part of UDPT. * This file is part of UDPT.
* *
* UDPT is free software: you can redistribute it and/or modify * UDPT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* UDPT is distributed in the hope that it will be useful, * UDPT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with UDPT. If not, see <http://www.gnu.org/licenses/>. * along with UDPT. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef UDPTRACKER_H_ #ifndef UDPTRACKER_H_
#define UDPTRACKER_H_ #define UDPTRACKER_H_
#include <stdint.h> #include <stdint.h>
#include <chrono> #include <chrono>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <list> #include <list>
#include <ctime> #include <ctime>
#include <boost/thread.hpp> #include <thread>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/log/trivial.hpp> #include <mutex>
#include <boost/log/sources/severity_channel_logger.hpp> #include <condition_variable>
#include "tools.h" #include "tools.h"
#include "exceptions.h" #include "exceptions.h"
#include "multiplatform.h" #include "multiplatform.h"
#include "db/driver_sqlite.hpp" #include "db/driver_sqlite.hpp"
#define UDPT_DYNAMIC (0x01) // Track Any info_hash? #define UDPT_DYNAMIC (0x01) // Track Any info_hash?
#define UDPT_ALLOW_REMOTE_IP (0x02) // Allow client's to send other IPs? #define UDPT_ALLOW_REMOTE_IP (0x02) // Allow client's to send other IPs?
#define UDPT_ALLOW_IANA_IP (0x04) // allow IP's like 127.0.0.1 or other IANA reserved IPs? #define UDPT_ALLOW_IANA_IP (0x04) // allow IP's like 127.0.0.1 or other IANA reserved IPs?
#define UDPT_VALIDATE_CLIENT (0x08) // validate client before adding to Database? (check if connection is open?) #define UDPT_VALIDATE_CLIENT (0x08) // validate client before adding to Database? (check if connection is open?)
namespace UDPT namespace UDPT
{ {
class UDPTracker class UDPTracker
{ {
public: public:
typedef struct udp_connection_request typedef struct udp_connection_request
{ {
uint64_t connection_id; uint64_t connection_id;
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
} ConnectionRequest; } ConnectionRequest;
typedef struct udp_connection_response typedef struct udp_connection_response
{ {
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
uint64_t connection_id; uint64_t connection_id;
} ConnectionResponse; } ConnectionResponse;
typedef struct udp_announce_request typedef struct udp_announce_request
{ {
uint64_t connection_id; uint64_t connection_id;
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
uint8_t info_hash [20]; uint8_t info_hash [20];
uint8_t peer_id [20]; uint8_t peer_id [20];
uint64_t downloaded; uint64_t downloaded;
uint64_t left; uint64_t left;
uint64_t uploaded; uint64_t uploaded;
uint32_t event; uint32_t event;
uint32_t ip_address; uint32_t ip_address;
uint32_t key; uint32_t key;
int32_t num_want; int32_t num_want;
uint16_t port; uint16_t port;
} AnnounceRequest; } AnnounceRequest;
typedef struct udp_announce_response typedef struct udp_announce_response
{ {
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
uint32_t interval; uint32_t interval;
uint32_t leechers; uint32_t leechers;
uint32_t seeders; uint32_t seeders;
uint8_t *peer_list_data; uint8_t *peer_list_data;
} AnnounceResponse; } AnnounceResponse;
typedef struct udp_scrape_request typedef struct udp_scrape_request
{ {
uint64_t connection_id; uint64_t connection_id;
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
uint8_t *torrent_list_data; uint8_t *torrent_list_data;
} ScrapeRequest; } ScrapeRequest;
typedef struct udp_scrape_response typedef struct udp_scrape_response
{ {
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
uint8_t *data; uint8_t *data;
} ScrapeResponse; } ScrapeResponse;
typedef struct udp_error_response typedef struct udp_error_response
{ {
uint32_t action; uint32_t action;
uint32_t transaction_id; uint32_t transaction_id;
char *message; char *message;
} ErrorResponse; } ErrorResponse;
enum StartStatus enum StartStatus
{ {
START_OK = 0, START_OK = 0,
START_ESOCKET_FAILED = 1, START_ESOCKET_FAILED = 1,
START_EBIND_FAILED = 2 START_EBIND_FAILED = 2
}; };
/** /**
* Initializes the UDP Tracker. * Initializes the UDP Tracker.
* @param settings Settings to start server with * @param settings Settings to start server with
*/ */
UDPTracker(const boost::program_options::variables_map& conf); UDPTracker(const boost::program_options::variables_map& conf);
/** /**
* Starts the Initialized instance. * Starts the Initialized instance.
*/ */
void start(); void start();
/** /**
* Terminates tracker. * Terminates tracker.
*/ */
void stop(); void stop();
/** /**
* Joins worker threads * Joins worker threads
*/ */
void wait(); void wait();
/** /**
* Destroys resources that were created by constructor * Destroys resources that were created by constructor
* @param usi Instance to destroy. * @param usi Instance to destroy.
*/ */
virtual ~UDPTracker(); virtual ~UDPTracker();
std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn; std::shared_ptr<UDPT::Data::DatabaseDriver> m_conn;
private: private:
SOCKET m_sock; SOCKET m_sock;
SOCKADDR_IN m_localEndpoint; SOCKADDR_IN m_localEndpoint;
uint16_t m_port; uint16_t m_port;
uint8_t m_threadCount; uint8_t m_threadCount;
bool m_isDynamic; bool m_isDynamic;
bool m_allowRemotes; bool m_allowRemotes;
bool m_allowIANA_IPs; bool m_allowIANA_IPs;
std::vector<boost::thread> m_threads; std::vector<std::thread> m_threads;
uint32_t m_announceInterval; uint32_t m_announceInterval;
uint32_t m_cleanupInterval; uint32_t m_cleanupInterval;
boost::log::sources::severity_channel_logger_mt<> m_logger; std::atomic_bool m_shouldRun;
const boost::program_options::variables_map& m_conf; std::mutex m_maintenanceMutex;
std::condition_variable m_maintenanceCondition;
static void _thread_start(UDPTracker *usi);
static void _maintainance_start(UDPTracker* usi); const boost::program_options::variables_map& m_conf;
static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r); static void _thread_start(UDPTracker *usi);
static void _maintainance_start(UDPTracker* usi);
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data); static int resolveRequest(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int r);
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int handleConnection(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &); static int handleAnnounce(UDPTracker *usi, SOCKADDR_IN *remote, char *data);
static int handleScrape(UDPTracker *usi, SOCKADDR_IN *remote, char *data, int len);
static int isIANAIP(uint32_t ip);
}; static int sendError(UDPTracker *, SOCKADDR_IN *remote, uint32_t transId, const std::string &);
};
static int isIANAIP(uint32_t ip);
#endif /* UDPTRACKER_H_ */ };
};
#endif /* UDPTRACKER_H_ */

55
tests/main.cpp Normal file
View file

@ -0,0 +1,55 @@
#include <gtest/gtest.h>
#include "../src/tools.h"
#include "../src/db/driver_sqlite.hpp"
TEST(Utility, SanityCheck) {
const uint32_t MAGIC = 0xDEADBEEF;
const unsigned char MAGIC_BYTES[4] = {0xEF, 0xBE, 0xAD, 0xDE};
ASSERT_TRUE(memcmp(&MAGIC, MAGIC_BYTES, 4) == 0);
}
TEST(Utility, CheckMTON) {
EXPECT_EQ(m_hton16(0xDEAD), 0xADDE);
EXPECT_EQ(m_hton32(0xDEADBEEF), 0xEFBEADDE);
EXPECT_EQ(m_hton64(0xDEADBEEFA1B2C3E4), 0xE4C3B2A1EFBEADDE);
}
TEST(Utility, HashToHexStr) {
const char EXPECTED_OUTPUT[] = "c670606edd22fd0e3b432c977559a687cc5d9bd2";
const unsigned char DATA[20] = {198, 112, 96, 110, 221, 34, 253, 14, 59, 67, 44, 151, 117, 89, 166, 135, 204, 93, 155, 210};
char OUTPUT_BUFFER[41] = {0};
to_hex_str(DATA, OUTPUT_BUFFER);
ASSERT_EQ(std::string(EXPECTED_OUTPUT), OUTPUT_BUFFER);
}
class SQLiteDriverTest:
public ::testing::Test {
protected:
SQLiteDriverTest(): va_map(), driver(nullptr) {
va_map.insert(std::pair<std::string, boost::program_options::variable_value>("db.param", boost::program_options::variable_value(std::string(":memory:"), true)));
}
virtual void SetUp() {
if (nullptr == driver) {
driver = new UDPT::Data::SQLite3Driver(va_map, false);
}
}
virtual void TearDown() {
if (nullptr != driver) {
delete driver;
driver = nullptr;
}
}
UDPT::Data::SQLite3Driver *driver;
boost::program_options::variables_map va_map;
};
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UDPT", "UDPT\UDPT.vcxproj", "{9F399AF8-861E-4E50-A94C-1388089E3FA0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Debug|Win32.ActiveCfg = Debug|Win32
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Debug|Win32.Build.0 = Debug|Win32
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Release|Win32.ActiveCfg = Release|Win32
{9F399AF8-861E-4E50-A94C-1388089E3FA0}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -1,111 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9F399AF8-861E-4E50-A94C-1388089E3FA0}</ProjectGuid>
<RootNamespace>UDPT</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\libs\sqlite3;D:\libs\boost\boost_1_59_0</IncludePath>
<LibraryPath>D:\libs\boost\boost_1_59_0\stage\lib;D:\libs\sqlite3\Release;$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\libs\sqlite3;D:\libs\boost\boost_1_59_0</IncludePath>
<LibraryPath>D:\libs\boost\boost_1_59_0\stage\lib;D:\libs\sqlite3\Release;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<ExceptionHandling>Async</ExceptionHandling>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;sqlite3.lib;advapi32.lib;shell32.lib</AdditionalDependencies>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<ExceptionHandling>Async</ExceptionHandling>
</ClCompile>
<Link>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<OptimizeReferences>
</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;sqlite3.lib;advapi32.lib;shell32.lib</AdditionalDependencies>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\src\db\database.cpp" />
<ClCompile Include="..\..\src\db\driver_sqlite.cpp" />
<ClCompile Include="..\..\src\http\httpserver.cpp" />
<ClCompile Include="..\..\src\http\webapp.cpp" />
<ClCompile Include="..\..\src\main.cpp" />
<ClCompile Include="..\..\src\service.cpp" />
<ClCompile Include="..\..\src\tools.c" />
<ClCompile Include="..\..\src\tracker.cpp" />
<ClCompile Include="..\..\src\udpTracker.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\db\database.hpp" />
<ClInclude Include="..\..\src\db\driver_sqlite.hpp" />
<ClInclude Include="..\..\src\exceptions.h" />
<ClInclude Include="..\..\src\http\httpserver.hpp" />
<ClInclude Include="..\..\src\http\webapp.hpp" />
<ClInclude Include="..\..\src\multiplatform.h" />
<ClInclude Include="..\..\src\service.hpp" />
<ClInclude Include="..\..\src\tools.h" />
<ClInclude Include="..\..\src\tracker.hpp" />
<ClInclude Include="..\..\src\udpTracker.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Header Files\db">
<UniqueIdentifier>{f5a85f67-2777-4fa5-828e-9c79af9f4b13}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\db">
<UniqueIdentifier>{643f664e-8751-4440-8b90-fe95b15e0aac}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\http">
<UniqueIdentifier>{bcb1fa9f-c9ec-4398-97f9-544baf69f2a9}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\http">
<UniqueIdentifier>{f65a2e69-6a71-4cd0-ad64-36a73c6c94c4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\tools.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\udpTracker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\db\database.cpp">
<Filter>Source Files\db</Filter>
</ClCompile>
<ClCompile Include="..\..\src\db\driver_sqlite.cpp">
<Filter>Source Files\db</Filter>
</ClCompile>
<ClCompile Include="..\..\src\http\httpserver.cpp">
<Filter>Source Files\http</Filter>
</ClCompile>
<ClCompile Include="..\..\src\http\webapp.cpp">
<Filter>Source Files\http</Filter>
</ClCompile>
<ClCompile Include="..\..\src\tracker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\service.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\multiplatform.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\tools.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\udpTracker.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\db\database.hpp">
<Filter>Header Files\db</Filter>
</ClInclude>
<ClInclude Include="..\..\src\db\driver_sqlite.hpp">
<Filter>Header Files\db</Filter>
</ClInclude>
<ClInclude Include="..\..\src\http\httpserver.hpp">
<Filter>Header Files\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\http\webapp.hpp">
<Filter>Header Files\http</Filter>
</ClInclude>
<ClInclude Include="..\..\src\exceptions.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\tracker.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\service.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>